Trying validate model and covariates with a single pig and then scaling up to all pigs at Tejon Ranch.

library(data.table)
library(raster)
library(yaml)
library(glmnet)
library(ggplot2)

Load in rasters and movement model

anal_params = yaml.load_file("analysis_parameters.yml")
dat = fread("~/Repos/rsf_swine/results/glmdata_by_study/tejon.csv")

Read 0.0% of 811256 rows
Read 35.7% of 811256 rows
Read 66.6% of 811256 rows
Read 98.6% of 811256 rows
Read 811256 rows and 77 (of 77) columns from 0.376 GB file in 00:00:06
# rawdat = fread("~/Repos/rsf_swine/data/formatted/full_pig_data.csv")
ras = raster("~/Repos/rsf_swine/data/covariate_data/croplayer/tejon/tejon_fruit_and_nuts_2015.tif")
cras = raster("~/Repos/rsf_swine/data/covariate_data/croplayer/tejon/tejon_cereals_2016.tif")
raswater = raster("~/Repos/rsf_swine/data/covariate_data/water/tejon/tejon_water_nndistance.tif")
rasel = raster("~/Repos/rsf_swine/data/covariate_data/elevation/tejon/tejon_elevation.tif")
rasndvi = raster("~/Repos/rsf_swine/data/covariate_data/ndvi/tejon/tejon_ndvi_4_2016.tif")
rasmast = raster("~/Repos/rsf_swine/data/covariate_data/masting/tejon/tejon_masting.tif")
rascover = raster("~/Repos/rsf_swine/data/covariate_data/landcover//tejon/tejon_canopycover.tif")
shpcrop = shapefile("~/Repos/rsf_swine/data/covariate_data/croplayer/tejon/tejon_fruit_and_nuts_2015.shp")
cshp = shapefile("~/Repos/rsf_swine/data/covariate_data/water/tejon/raw/tejon_water_nwi_raw.shp")
cshp_trans = spTransform(cshp, CRS("+proj=longlat +datum=WGS84"))
# Only consider permanent and semi-permanent water sources: "H" and "F" in NWI lingo: https://www.fws.gov/wetlands/Data/Wetland-Codes.html
peren = cshp_trans[grepl("H", cshp_trans$ATTRIBUTE) | grepl("F", cshp_trans$ATTRIBUTE), ]
unique(dat$pigID)
 [1] "tejonF02_F12" "tejonF16"     "tejonF17"     "tejonF05"     "tejonF07"     "tejonF04"     "tejonF06"    
 [8] "tejonM301"    "tejonM03"     "tejonM306"    "tejonM302"    "tejonM307"    "tejonM401"   

Visualize the pig movement

tdat = dat[pigID == "tejonM302"]
tdat[ , datetime:=as.POSIXct(t, origin = '1970-01-01', tz = 'GMT')]
plot(crop(rascover, ras))
#plot(ras, col=c("white", "blue"), add=T)
plot(shpcrop, add=T)
plot(peren, add=T, border='blue')
points(tdat[, x.adj], tdat[, y.adj], cex=0.01, col="red", pch=19)
points(tdat[crop_loc == 1, x.adj], tdat[crop_loc == 1, y.adj], cex=0.1, col="gray", pch=19)

#points(tdat[, x.adj], tdat[, y.adj], cex=0.01, col="red", pch=19)
#points(tdat[crop_loc == 1, x.adj], tdat[crop_loc == 1, y.adj], cex=0.05, col="blue", pch=19)
#plot(sp, add=T)

Fit a single, crop-using pig

First just look at the effects of movement independent of hour

require(doMC)
source("pigfxns.R")
tdat = dat[pigID == "tejonM302"]
tdat[ , datetime:=as.POSIXct(t, origin = '1970-01-01', tz = 'GMT')]
Xfull = build_design_matrix2(tdat, c("ndvi_loc", "ndvi_grad", "masting_loc",
                                    "masting_grad", "water_grad",
                                    "elevation_loc", "elevation_grad",
                                    "fruit_and_nutsdists_grad",
                                    "canopycover_loc", "canopycover_grad", "crw"), 
                                    c("fruit_and_nuts_loc"))
registerDoMC(cores=4)
tfit = cv.glmnet(Xfull, y=tdat$z, offset=log(tdat$tau), 
                       family="poisson", 
                       alpha=1, nlambda=20, nfolds=4, parallel=TRUE)
tfit$glmnet.fit$beta[, which(tfit$lambda == tfit$lambda.min), drop=F]
12 x 1 sparse Matrix of class "dgCMatrix"
                                  s9
fruit_and_nuts_loc       -1.13508408
ndvi_loc                 -0.03737648
ndvi_grad                 .         
masting_loc               0.32589925
masting_grad              .         
water_grad               -0.02177166
elevation_loc            -0.58012665
elevation_grad           -0.04664426
fruit_and_nutsdists_grad -0.01619372
canopycover_loc          -0.49927946
canopycover_grad          .         
crw                       0.96506653

Substantially slower movement once the pig is in a crop field. An overall tendency to move toward crop fields (NOTE: this is highly dependent on the time of day!). A tendency to stay longer in higher cover

Fit a stan model to this data

library(rstan)

poisglm = stan_model("stan_files/poisson_glm.stan")
Xfullstan = cbind(1, Xfull)
sdata = list(X=Xfullstan, N=nrow(Xfullstan), p=ncol(Xfullstan), z=tdat$z, tau=tdat$tau)
fitpglm = sampling(poisglm, data=sdata, chains=3, iter=2000)

Let’s add in daily variation in movement behavior.

tdat[ , datetime:=as.POSIXct(t, origin = '1970-01-01', tz = 'GMT')]
tdat[ , hourofday:=scale(hour(datetime))]
df_hour = 5
stdcols = c("ndvi_loc", "ndvi_grad", "masting_loc",
            "masting_grad", "water_grad",
            "elevation_loc", "elevation_grad",
            "fruit_and_nutsdists_grad",
            "canopycover_loc", "canopycover_grad", "crw")
nonstdcols = c("fruit_and_nuts_loc")
splinecols = c("ndvi_grad", "masting_grad", "elevation_grad", "fruit_and_nutsdists_grad",
                "canopycover_grad", "water_grad", "crw")
Xgam = build_daily_spline_design_matrix(tdat, stdcols, nonstdcols, splinecols, df_hour=df_hour)
registerDoMC(cores=4)
fitgam = cv.glmnet(Xgam, y=tdat$z, offset=log(tdat$tau), family="poisson", 
                       alpha=1, nlambda=20, nfolds=5, parallel=TRUE)
bestbetas = fitgam$glmnet.fit$beta[, which(fitgam$lambda == fitgam$lambda.min), drop=T]
bestbetas
        fruit_and_nuts_loc                   ndvi_loc                masting_loc              elevation_loc            canopycover_loc 
              -1.280810173                0.015654860                0.248419324               -0.485084162               -0.388919143 
               base_hour_1                base_hour_2                base_hour_3                base_hour_4                ndvi_grad_1 
               0.059019209                0.000000000               -1.875606721               -0.310093840                0.000000000 
               ndvi_grad_2                ndvi_grad_3                ndvi_grad_4             masting_grad_1             masting_grad_2 
               0.051172445                0.156166594                0.095426297               -0.006868112               -0.038413037 
            masting_grad_3             masting_grad_4           elevation_grad_1           elevation_grad_2           elevation_grad_3 
               0.097975291               -0.001155836               -0.116678032                0.155253611               -0.056037462 
          elevation_grad_4 fruit_and_nutsdists_grad_1 fruit_and_nutsdists_grad_2 fruit_and_nutsdists_grad_3 fruit_and_nutsdists_grad_4 
              -0.068425762               -0.253919885                0.659598405                0.256502244               -0.517798945 
        canopycover_grad_1         canopycover_grad_2         canopycover_grad_3         canopycover_grad_4               water_grad_1 
              -0.023065962                0.057412865               -0.026700225               -0.018683890                0.042630933 
              water_grad_2               water_grad_3               water_grad_4                      crw_1                      crw_2 
              -0.214278962               -0.006450523                0.126489338                0.938400820                0.868051537 
                     crw_3                      crw_4 
              -0.106011018                0.746115211 

Plot the daily effects

library(ggplot2)
splinehour = s(hourofday, bs="cc", k=df_hour)
pred_hours = Predict.matrix(smooth.construct2(splinehour, tdat, NULL), data.frame(hourofday=sort(unique(tdat$hourofday)))) # B-spline basis for hour effect
dailyres = list()
plist = c("base_hour_*", "ndvi_grad_*", "masting_grad_*", "elevation_grad_*", "fruit_and_nutsdists_grad_*",
                                                    "canopycover_grad_*", "water_grad_*", "crw_*")
#plist = c("canopycover_loc_hour_*")
for(pattern in plist) {
  
  eff = pred_hours %*% t(t(bestbetas[names(bestbetas) %like% pattern]))
  effdt = data.frame(hour=0:23, beta=eff, coef=pattern)
  
  dailyres[[pattern]] = effdt
}
dailyres = do.call(rbind, dailyres)
tplot = ggplot(dailyres) + geom_line(aes(x=hour, y=beta)) + xlab("Hour of day") + ylab("Effect of on pig movement") + theme_bw() + facet_wrap(~coef, scales="free")
print(tplot)

#ggsave("../docs/presentations/images/canopycoverloc.pdf", width=5, height =3)

This is exactly what we would expect based on a empirical exploration of the movement patterns. A strong tendency to move toward the used fruit and nut fields during the evening hours and thi tendency reverses in the in the early morning hours where the pig is moving away from these fields. Because the fields are in low NDVI areas, there is a strong correlation


Fit model to all pigs.

unique(dat$pigID)
allres = list()
nongam = list()
allbestbetas = list()
for(pig in unique(dat$pigID)){
  
  # Extract a format data
  tdat = dat[pigID == pig]
  cat("Fitting pig", pig, "\n")
  tdat[ , datetime:=as.POSIXct(t, origin = '1970-01-01', tz = 'GMT')]
  tdat[ , hourofday:=scale(hour(datetime))]
  
  # Set fngrad to 0 if it is NA
  if(all(is.na(tdat$fruit_and_nutsdists_grad)))
     fngrad = rep(0, nrow(tdat))
  else
    fngrad = tdat$fruit_and_nutsdists_grad
  
  tdat$fruit_and_nutsdists_grad = fngrad
  # Step 1: Fit the non-daily model
  Xfull = build_design_matrix2(tdat, c("ndvi_loc", "ndvi_grad", "masting_loc",
                                    "masting_grad", "water_grad",
                                    "elevation_loc", "elevation_grad",
                                    "fruit_and_nutsdists_grad",
                                    "canopycover_loc", "canopycover_grad", "crw"), 
                                    c("fruit_and_nuts_loc"))
  registerDoMC(cores=4)
  tfit = cv.glmnet(Xfull, y=tdat$z, offset=log(tdat$tau), 
                       family="poisson", 
                       alpha=1, nlambda=20, nfolds=4, parallel=TRUE)
  
  nongam[[pig]] = tfit$glmnet.fit$beta[, which(tfit$lambda == tfit$lambda.min), drop=T]
  
  # Step 2: Fit the daily model
  stdcols = c("ndvi_loc", "ndvi_grad", "masting_loc",
              "masting_grad", "water_grad",
              "elevation_loc", "elevation_grad",
              "fruit_and_nutsdists_grad",
              "canopycover_loc", "canopycover_grad", "crw")
  nonstdcols = c("fruit_and_nuts_loc")
  splinecols = c("ndvi_grad", "masting_grad", "elevation_grad", "fruit_and_nutsdists_grad",
                 "canopycover_grad", "water_grad", "crw")
  
  if(pig == "tejonM302")
    Xtemp = Xgam
  
  Xgam = build_daily_spline_design_matrix(tdat, stdcols, nonstdcols, splinecols, df_hour=5)
  
  registerDoMC(cores=4)
  fitgam = cv.glmnet(Xgam, y=tdat$z, offset=log(tdat$tau), family="poisson", 
                     alpha=1, nlambda=20, nfolds=5, parallel=TRUE)
  
  bestbetas = fitgam$glmnet.fit$beta[, which(fitgam$lambda == fitgam$lambda.1se), drop=T]
  allbestbetas[[pig]] = bestbetas
  
  # Step 3: Extract cyclic basis for hour effect
  splinehour = s(hourofday, bs="cc", k=df_hour)
  pred_hours = Predict.matrix(smooth.construct2(splinehour, tdat, NULL), data.frame(hourofday=sort(unique(tdat$hourofday))))
  
  dailyres = list()
  plist = c("base_hour_*", "ndvi_grad_*", "masting_grad_*", "elevation_grad_*", "fruit_and_nutsdists_grad_*",
                                                    "canopycover_grad_*", "water_grad_*", "crw_*")
  for(pattern in plist) {
    
    eff = pred_hours %*% t(t(bestbetas[names(bestbetas) %like% pattern]))
    effdt = data.frame(hour=0:23, beta=eff, coef=pattern)
    dailyres[[pattern]] = effdt
  }
  
  dailyres = do.call(rbind, dailyres)
  dailyres$pigID = pig
  cat("Saving", pig, "\n")
  allres[[pig]] = dailyres
  
}
Fitting pig tejonF02_F12 
Saving tejonF02_F12 
Fitting pig tejonF16 
Saving tejonF16 
Fitting pig tejonF17 
Saving tejonF17 
Fitting pig tejonF05 
Saving tejonF05 
Fitting pig tejonF07 
Saving tejonF07 
Fitting pig tejonF04 
Saving tejonF04 
Fitting pig tejonF06 
Saving tejonF06 
Fitting pig tejonM301 
Saving tejonM301 
Fitting pig tejonM03 
Saving tejonM03 
Fitting pig tejonM306 
Saving tejonM306 
Fitting pig tejonM302 
Saving tejonM302 
Fitting pig tejonM307 
Saving tejonM307 
Fitting pig tejonM401 
Saving tejonM401 
allres_dt = as.data.table(do.call(rbind, allres))
ggplot(allres_dt) + geom_line(aes(x=hour, y=beta, color=pigID)) + xlab("Hour of day") + ylab("Effect on hourly movement rate") + theme_bw() + facet_wrap(~coef, scales="free")

locparams = sapply(allbestbetas, function(x) x[names(x) %like% "*_loc"])
mlocparams = melt(locparams)
colnames(mlocparams) = c("coef", "pig", "value")
ggplot(mlocparams) + geom_boxplot(aes(x=coef, y=value)) + theme_bw()

A consistent tendecy to remain longer in higher NDVI cells. No consistent effect of elevation or masting. Considering how seasonal masting is, this is not altogether surprising. Canopy cover is, a bit more surprisingly much more variable.

nongamparams = do.call(rbind, nongam)
mparams = melt(nongamparams)
colnames(mparams) = c("pig", "coef", "value")
ggplot(mparams) + geom_boxplot(aes(x=coef, y=value)) + theme_bw() + theme(axis.text.x = element_text(angle = 90, hjust = 1))

There are two pigs that seem to be doing some weird things: tejonF07 and tejonF05. Let’s look at these a bit more closely.

require(lubridate)
tF07 = dat[pigID == "tejonF05"]
tF07[, datetime:=as.POSIXct(t, origin = '1970-01-01', tz = 'GMT')]

max(tF07$datetime) - min(tF07$datetime)
plot(tF07$datetime, rep(1, nrow(tF07)))
hist(hour(tF07$datetime))

For these pigs, it does seem that they are moving much more during the day than other pigs in the population. Why is that?

Explore a PCA analysis on the variables for different pigs to look for simiarlities and differences

library(ggfortify)
library(ggplot2)
allparams = do.call(rbind, allbestbetas)
# Drop columns that are all 0
not0 = !apply(allparams, 2, function(x) all(x == 0))
redparams = allparams[, not0]
redparams_dt = as.data.frame(redparams)
redparams_dt$pigID = rownames(redparams)
pcares = stats::prcomp(redparams, scale=T)
autoplot(pcares, data = redparams_dt, colour = 'pigID',
         loadings = T, loadings.colour = 'blue',
         loadings.label = T, loadings.label.size = 2, label=TRUE, label.size=3)

#(pcares$sdev)^2 / sum((pcares$sdev)^2)
#barplot(pcares$rotation[, 2][order(abs(pcares$rotation[, 2]))])
locparams = redparams[ , colnames(redparams) %like% "*_loc"]
for(p in plist){
  sump = t(t(rowSums(redparams[, colnames(redparams) %like% p])))
  locparams = cbind(sump, locparams)
  colnames(locparams)[1] = p
}
locparams = locparams[, !(colnames(locparams) %in% c("crw_*", "base_hour_*"))]
locparams_dt = as.data.frame(locparams)
locparams_dt$pigID = rownames(locparams)
ploc = stats::prcomp(locparams, scale=T)
autoplot(ploc, data = locparams_dt, colour = 'pigID',
         loadings = T, loadings.colour = 'blue',
         loadings.label = T, loadings.label.size = 2, label=TRUE, label.size=3)

Add in seasonal effects for Tejon pigs

We have considered daily effects of movement, no let’s consider seasonal (i.e. monthly effects on movement). Two approaches for doing this

  1. Fit a montly spline
  2. Break the data into 4 seasons and fit a “season” factor that interacts with daily effects.

Predicted seasonal covariates

Likely seasonal covariates

  1. Crop gradient
  2. NDVI (this is already varying monthly)
  • Gradient and location
  1. Masting density
  • Gradient and location
  1. Water usage
  • Gradient
Xseason = build_seasonal_design_matrix(tdat, stdcols, nonstdcols, splinecols, seasonalcols, df_hour=df_hour)
     summer fall winter spring
[1,]      1    0      0      0
[2,]      1    0      0      0
[3,]      1    0      0      0
[4,]      1    0      0      0
[5,]      1    0      0      0
[6,]      1    0      0      0
Error in seasonmat[, season] : subscript out of bounds
bestbetas = fitseason$glmnet.fit$beta[, which(fitseason$lambda == fitseason$lambda.min), drop=T]
bestbetas
              water_grad_1:spring               water_grad_2:spring               water_grad_3:spring               water_grad_4:spring 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
              water_grad_1:winter               water_grad_2:winter               water_grad_3:winter               water_grad_4:winter 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
                water_grad_1:fall                 water_grad_2:fall                 water_grad_3:fall                 water_grad_4:fall 
                      0.040594515                      -0.208389961                       0.075296108                       0.090820608 
              water_grad_1:summer               water_grad_2:summer               water_grad_3:summer               water_grad_4:summer 
                      0.000000000                      -0.224349345                      -0.467104193                       0.244472113 
                  ndvi_loc:spring                   ndvi_loc:winter                     ndvi_loc:fall                   ndvi_loc:summer 
                      0.000000000                       0.000000000                      -0.040763710                       0.256240445 
               ndvi_grad_1:spring                ndvi_grad_2:spring                ndvi_grad_3:spring                ndvi_grad_4:spring 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
               ndvi_grad_1:winter                ndvi_grad_2:winter                ndvi_grad_3:winter                ndvi_grad_4:winter 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
                 ndvi_grad_1:fall                  ndvi_grad_2:fall                  ndvi_grad_3:fall                  ndvi_grad_4:fall 
                      0.015108503                       0.039760177                       0.200690524                       0.122868885 
               ndvi_grad_1:summer                ndvi_grad_2:summer                ndvi_grad_3:summer                ndvi_grad_4:summer 
                     -0.003235823                       0.147947903                       0.148405816                      -0.006732353 
               masting_loc:spring                masting_loc:winter                  masting_loc:fall                masting_loc:summer 
                      0.000000000                       0.000000000                       0.332703924                       0.060485702 
            masting_grad_1:spring             masting_grad_2:spring             masting_grad_3:spring             masting_grad_4:spring 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
            masting_grad_1:winter             masting_grad_2:winter             masting_grad_3:winter             masting_grad_4:winter 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
              masting_grad_1:fall               masting_grad_2:fall               masting_grad_3:fall               masting_grad_4:fall 
                     -0.040016285                      -0.016222331                       0.055214070                      -0.035491126 
            masting_grad_1:summer             masting_grad_2:summer             masting_grad_3:summer             masting_grad_4:summer 
                      0.013566022                      -0.076122075                       0.122760423                       0.117177137 
fruit_and_nutsdists_grad_1:spring fruit_and_nutsdists_grad_2:spring fruit_and_nutsdists_grad_3:spring fruit_and_nutsdists_grad_4:spring 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
fruit_and_nutsdists_grad_1:winter fruit_and_nutsdists_grad_2:winter fruit_and_nutsdists_grad_3:winter fruit_and_nutsdists_grad_4:winter 
                      0.000000000                       0.000000000                       0.000000000                       0.000000000 
  fruit_and_nutsdists_grad_1:fall   fruit_and_nutsdists_grad_2:fall   fruit_and_nutsdists_grad_3:fall   fruit_and_nutsdists_grad_4:fall 
                     -0.287264857                       0.681829141                       0.241771677                      -0.469621767 
fruit_and_nutsdists_grad_1:summer fruit_and_nutsdists_grad_2:summer fruit_and_nutsdists_grad_3:summer fruit_and_nutsdists_grad_4:summer 
                     -0.070339777                       0.547666876                       0.266771894                      -0.796233551 
               fruit_and_nuts_loc                     elevation_loc                   canopycover_loc                       base_hour_1 
                     -1.272343825                      -0.465029001                      -0.397791335                       0.082369122 
                      base_hour_2                       base_hour_3                       base_hour_4                  elevation_grad_1 
                      0.000000000                      -1.896707767                      -0.315994956                      -0.100603955 
                 elevation_grad_2                  elevation_grad_3                  elevation_grad_4                canopycover_grad_1 
                      0.146299585                      -0.024508938                      -0.057671454                      -0.017052913 
               canopycover_grad_2                canopycover_grad_3                canopycover_grad_4                             crw_1 
                      0.047664304                      -0.010897735                      -0.011045384                       0.939725265 
                            crw_2                             crw_3                             crw_4                            summer 
                      0.863646471                      -0.119535231                       0.747834212                      -0.107865468 
                             fall                            winter                            spring 
                      0.000000000                       0.000000000                       0.000000000 

Plot the seasonal location effects

# Which seasons was the pig in?
rsums = apply(Xseason[, names(seasons)], 2, sum)
inseason = names(rsums)[rsums != 0]
# Location effects
locparams = bestbetas[names(bestbetas) %like% "*loc*"]
unqparams = unique(do.call(c, lapply(strsplit(names(locparams), ":"), function(x) x[[1]])))
for(prm in unqparams){
  print(locparams[names(locparams) %like% prm])
}
ndvi_loc:spring ndvi_loc:winter   ndvi_loc:fall ndvi_loc:summer 
     0.00000000      0.00000000     -0.04010893      0.13396043 
masting_loc:spring masting_loc:winter   masting_loc:fall masting_loc:summer 
         0.0000000          0.0000000          0.2182664          0.0000000 
fruit_and_nuts_loc 
        -0.9162766 
elevation_loc 
   -0.3546265 
canopycover_loc 
     -0.3968152 
splinehour = s(hourofday, bs="cc", k=df_hour)
pred_hours = Predict.matrix(smooth.construct2(splinehour, tdat, NULL), data.frame(hourofday=sort(unique(tdat$hourofday)))) # B-spline basis for hour effect
dailyres = list()
plist = c("base_hour_*", "elevation_grad_*","canopycover_grad_*", "crw_*")
#plist = c("canopycover_loc_hour_*")
for(pattern in plist) {
  
  eff = pred_hours %*% t(t(bestbetas[names(bestbetas) %like% pattern]))
  effdt = data.frame(hour=0:23, beta=eff, coef=pattern)
  
  dailyres[[pattern]] = effdt
}
dailyres = do.call(rbind, dailyres)
tplot = ggplot(dailyres) + geom_line(aes(x=hour, y=beta)) + xlab("Hour of day") + ylab("Effect of on pig movement") + theme_bw() + facet_wrap(~coef, scales="free")
print(tplot)

splinehour = s(hourofday, bs="cc", k=df_hour)
pred_hours = Predict.matrix(smooth.construct2(splinehour, tdat, NULL), data.frame(hourofday=sort(unique(tdat$hourofday)))) # B-spline basis for hour effect
dailyres = list()
rsums = apply(Xseason[, names(seasons)], 2, sum)
inseason = names(rsums)[rsums != 0]
plist = c("ndvi_grad_*", "masting_grad_*", "fruit_and_nutsdists_grad_*", "water_grad_*")
#plist = c("canopycover_loc_hour_*")
for(pattern in plist) {
  
  dailyres[[pattern]] = list()
  seasbetas = bestbetas[names(bestbetas) %like% pattern]
  
  for(season in inseason){
    eff = pred_hours %*% t(t(seasbetas[names(seasbetas) %like% season]))
    effdt = data.frame(hour=0:23, beta=eff, coef=pattern)
    effdt$season = season
    dailyres[[pattern]][[season]] = effdt
  }
}
seasonres = do.call(rbind, lapply(dailyres, function(x) do.call(rbind, x)))
tplot = ggplot(seasonres) + geom_line(aes(x=hour, y=beta, linetype=season)) + xlab("Hour of day") + ylab("Effect of on pig movement") + theme_bw() + facet_wrap(~coef, scales="free")
print(tplot)

Fit seasonal model for all pigs

source("pigfxns.R")
allseasbetas = list()
splinelist = list()
for(pig in unique(dat$pigID)){
  
  cat("Fitting", pig, "\n")
  tdat = dat[pigID == pig]
  tdat[ , datetime:=as.POSIXct(t, origin = '1970-01-01', tz = 'GMT')]
  tdat[ , hourofday:=scale(hour(datetime))]
  tdat[ , monthofyear:=month(datetime)]
  
  # Set fngrad to 0 if it is NA
  if(all(is.na(tdat$fruit_and_nutsdists_grad)))
     fngrad = rep(0, nrow(tdat))
  else
    fngrad = tdat$fruit_and_nutsdists_grad
  
  tdat$fruit_and_nutsdists_grad = fngrad
  
  
  seasons = list("summer" = c(6, 7, 8), "fall" = c(9, 10, 11), 
                 "winter"= c(12, 1, 2), "spring"=c(3, 4, 5))
  
  df_hour = 5
  stdcols = c("ndvi_loc", "ndvi_grad", "masting_loc",
              "masting_grad", "water_grad",
              "elevation_loc", "elevation_grad",
              "fruit_and_nutsdists_grad",
              "canopycover_loc", "canopycover_grad", "crw")
  nonstdcols = c("fruit_and_nuts_loc")
  splinecols = c("ndvi_grad", "masting_grad", "elevation_grad", "fruit_and_nutsdists_grad",
                 "canopycover_grad", "water_grad", "crw")
  seasonalcols = c("fruit_and_nutsdists_grad", "masting_grad", "masting_loc", "ndvi_grad", "ndvi_loc", "water_grad",
                    "crw")
  Xseason = build_seasonal_design_matrix(tdat, stdcols, nonstdcols, splinecols, seasonalcols, df_hour=df_hour)
  
  registerDoMC(cores=4)
  fitseason = cv.glmnet(Xseason, y=tdat$z, offset=log(tdat$tau), family="poisson", 
                        alpha=1, nlambda=20, nfolds=5, parallel=TRUE)
  
  bestbetas = fitseason$glmnet.fit$beta[, which(fitseason$lambda == fitseason$lambda.1se), drop=T]
  
  allseasbetas[[pig]] = bestbetas
  
  splinehour = s(hourofday, bs="cc", k=df_hour)
  pred_hours = Predict.matrix(smooth.construct2(splinehour, tdat, NULL), data.frame(hourofday=sort(unique(tdat$hourofday)))) # B-spline basis for hour effect
  
  dailyres = list()
  rsums = apply(Xseason[, names(seasons)], 2, sum)
  inseason = names(rsums)[rsums != 0]
  
  plist = c("ndvi_grad_*", "masting_grad_*", "fruit_and_nutsdists_grad_*", "water_grad_*", "crw_*", "base_hour_*")
  #plist = c("canopycover_loc_hour_*")
  for(pattern in plist) {
    
    dailyres[[pattern]] = list()
    seasbetas = bestbetas[names(bestbetas) %like% pattern]
    
    for(season in inseason){
      eff = pred_hours %*% t(t(seasbetas[names(seasbetas) %like% season]))
      effdt = data.frame(hour=0:23, beta=eff, coef=pattern)
      effdt$season = season
      dailyres[[pattern]][[season]] = effdt
    }
  }
  
  seasonres = do.call(rbind, lapply(dailyres, function(x) do.call(rbind, x)))
  seasonres$pigID = pig
  splinelist[[pig]] = seasonres
  
}
Fitting tejonF02_F12 
Fitting tejonF16 
Fitting tejonF17 
Fitting tejonF05 
Fitting tejonF07 
Fitting tejonF04 
Fitting tejonF06 
Fitting tejonM301 
Fitting tejonM03 
Fitting tejonM306 
Fitting tejonM302 
Fitting tejonM307 
Fitting tejonM401 

allpigs_seasoncoef = as.data.table(do.call(rbind, splinelist))
allpigs_seasoncoef$season = factor(allpigs_seasoncoef$season, levels=c("winter", "spring", "summer", "fall"))
meanpigs = allpigs_seasoncoef[, list(meanbeta=mean(beta)), by=list(coef, season, hour)]
meanpigs$season = factor(meanpigs$season, levels=c("winter", "spring", "summer", "fall"))

tplot = ggplot(data=NULL) + geom_line(data=allpigs_seasoncoef, aes(x=hour, y=beta, color=pigID), alpha=0.5) + geom_line(data=meanpigs, aes(x=hour, y=meanbeta), size=1) + 
                    xlab("Hour of day") + ylab("Effect of on pig movement") + theme_bw() + facet_wrap(~coef + season, scales="free", nrow=6, ncol=4)
print(tplot)

Plot the variable location coefficients by season

Similar to the previous analysis. We see an increased tendency to linger in areas with high masting tree density in the winter (though this might be driven by very few points). The remainder of the year there is not as much selection for areas with high masting density.

Temperature and precipitation analysis

Instead of using a seasonal factor as we do in the previous analysis, let’s replace this seasonal factor with an interaction between monthly temperature and precipitation and our seasonal variables.

registerDoMC(cores=4)
Error in registerDoMC(cores = 4) : could not find function "registerDoMC"
bestbetas = fitseason$glmnet.fit$beta[, which(fitseason$lambda == fitseason$lambda.1se), drop=T]
bestbetas
               base_hour_1:temperature_loc:precipitation_loc                base_hour_2:temperature_loc:precipitation_loc 
                                                0.0509413791                                                -0.0064986636 
               base_hour_3:temperature_loc:precipitation_loc                base_hour_4:temperature_loc:precipitation_loc 
                                                0.5992985349                                                -0.0698370964 
                               base_hour_1:precipitation_loc                                base_hour_2:precipitation_loc 
                                                0.2879690962                                                -0.4268504980 
                               base_hour_3:precipitation_loc                                base_hour_4:precipitation_loc 
                                               -0.3028297767                                                 0.0000000000 
                                 base_hour_1:temperature_loc                                  base_hour_2:temperature_loc 
                                                0.3407091755                                                -0.7064455969 
                                 base_hour_3:temperature_loc                                  base_hour_4:temperature_loc 
                                               -0.3899467426                                                 0.0000000000 
                     crw_1:temperature_loc:precipitation_loc                      crw_2:temperature_loc:precipitation_loc 
                                                0.0000000000                                                -0.0932738347 
                     crw_3:temperature_loc:precipitation_loc                      crw_4:temperature_loc:precipitation_loc 
                                                0.1068090501                                                -0.1154302852 
                                     crw_1:precipitation_loc                                      crw_2:precipitation_loc 
                                                0.2077015754                                                 0.0000000000 
                                     crw_3:precipitation_loc                                      crw_4:precipitation_loc 
                                                0.2493275989                                                 0.0000000000 
                                       crw_1:temperature_loc                                        crw_2:temperature_loc 
                                                0.1008813596                                                -0.1388014272 
                                       crw_3:temperature_loc                                        crw_4:temperature_loc 
                                                0.0000000000                                                -0.0475675094 
              water_grad_1:temperature_loc:precipitation_loc               water_grad_2:temperature_loc:precipitation_loc 
                                                0.0000000000                                                -0.0571194608 
              water_grad_3:temperature_loc:precipitation_loc               water_grad_4:temperature_loc:precipitation_loc 
                                                0.0798016211                                                 0.0000000000 
                              water_grad_1:precipitation_loc                               water_grad_2:precipitation_loc 
                                               -0.0024288806                                                 0.0950040054 
                              water_grad_3:precipitation_loc                               water_grad_4:precipitation_loc 
                                                0.0000000000                                                -0.1186417172 
                                water_grad_1:temperature_loc                                 water_grad_2:temperature_loc 
                                               -0.0081758525                                                 0.0000000000 
                                water_grad_3:temperature_loc                                 water_grad_4:temperature_loc 
                                               -0.1770103227                                                 0.0000000000 
                  ndvi_loc:temperature_loc:precipitation_loc                                   ndvi_loc:precipitation_loc 
                                               -0.1075859961                                                 0.0657222466 
                                    ndvi_loc:temperature_loc                ndvi_grad_1:temperature_loc:precipitation_loc 
                                               -0.0120984258                                                 0.0000000000 
               ndvi_grad_2:temperature_loc:precipitation_loc                ndvi_grad_3:temperature_loc:precipitation_loc 
                                                0.0000000000                                                -0.2321655943 
               ndvi_grad_4:temperature_loc:precipitation_loc                                ndvi_grad_1:precipitation_loc 
                                                0.0008217166                                                -0.0027839473 
                               ndvi_grad_2:precipitation_loc                                ndvi_grad_3:precipitation_loc 
                                               -0.1179538809                                                 0.0000000000 
                               ndvi_grad_4:precipitation_loc                                  ndvi_grad_1:temperature_loc 
                                                0.0068035766                                                 0.0000000000 
                                 ndvi_grad_2:temperature_loc                                  ndvi_grad_3:temperature_loc 
                                               -0.2217073313                                                 0.0000000000 
                                 ndvi_grad_4:temperature_loc                masting_loc:temperature_loc:precipitation_loc 
                                                0.0000000000                                                 0.2502237969 
                               masting_loc:precipitation_loc                                  masting_loc:temperature_loc 
                                                0.0000000000                                                -0.0407435744 
            masting_grad_1:temperature_loc:precipitation_loc             masting_grad_2:temperature_loc:precipitation_loc 
                                                0.0210441721                                                 0.0000000000 
            masting_grad_3:temperature_loc:precipitation_loc             masting_grad_4:temperature_loc:precipitation_loc 
                                                0.0000000000                                                 0.1484524078 
                            masting_grad_1:precipitation_loc                             masting_grad_2:precipitation_loc 
                                               -0.0502199947                                                 0.0341172459 
                            masting_grad_3:precipitation_loc                             masting_grad_4:precipitation_loc 
                                               -0.0714737164                                                 0.0000000000 
                              masting_grad_1:temperature_loc                               masting_grad_2:temperature_loc 
                                                0.0306776738                                                 0.0000000000 
                              masting_grad_3:temperature_loc                               masting_grad_4:temperature_loc 
                                               -0.0964010141                                                 0.0348303498 
fruit_and_nutsdists_grad_1:temperature_loc:precipitation_loc fruit_and_nutsdists_grad_2:temperature_loc:precipitation_loc 
                                                0.0000000000                                                 0.0000000000 
fruit_and_nutsdists_grad_3:temperature_loc:precipitation_loc fruit_and_nutsdists_grad_4:temperature_loc:precipitation_loc 
                                               -0.3401670539                                                 0.1872092138 
                fruit_and_nutsdists_grad_1:precipitation_loc                 fruit_and_nutsdists_grad_2:precipitation_loc 
                                                0.1235313483                                                -0.0810581190 
                fruit_and_nutsdists_grad_3:precipitation_loc                 fruit_and_nutsdists_grad_4:precipitation_loc 
                                                0.0561933996                                                -0.0874596649 
                  fruit_and_nutsdists_grad_1:temperature_loc                   fruit_and_nutsdists_grad_2:temperature_loc 
                                                0.1425060741                                                 0.0000000000 
                  fruit_and_nutsdists_grad_3:temperature_loc                   fruit_and_nutsdists_grad_4:temperature_loc 
                                               -0.3269487121                                                 0.0000000000 
                                          fruit_and_nuts_loc                                                     ndvi_loc 
                                               -1.0611488112                                                -0.2572557622 
                                                 masting_loc                                                elevation_loc 
                                                0.5430828136                                                -0.4178863853 
                                             canopycover_loc                                                  base_hour_1 
                                               -0.3318493700                                                 0.2264697186 
                                                 base_hour_2                                                  base_hour_3 
                                                0.0000000000                                                -1.7057952482 
                                                 base_hour_4                                                  ndvi_grad_1 
                                               -0.3282004231                                                 0.0000000000 
                                                 ndvi_grad_2                                                  ndvi_grad_3 
                                                0.0000000000                                                 0.0000000000 
                                                 ndvi_grad_4                                               masting_grad_1 
                                                0.0148908877                                                -0.0037137117 
                                              masting_grad_2                                               masting_grad_3 
                                               -0.0356481982                                                 0.0000000000 
                                              masting_grad_4                                             elevation_grad_1 
                                                0.0996230927                                                -0.0756440683 
                                            elevation_grad_2                                             elevation_grad_3 
                                                0.0975331862                                                 0.0000000000 
                                            elevation_grad_4                                   fruit_and_nutsdists_grad_1 
                                               -0.1028042822                                                -0.2602659910 
                                  fruit_and_nutsdists_grad_2                                   fruit_and_nutsdists_grad_3 
                                                0.6584975732                                                -0.0220173582 
                                  fruit_and_nutsdists_grad_4                                           canopycover_grad_1 
                                               -0.3620918625                                                -0.0023753740 
                                          canopycover_grad_2                                           canopycover_grad_3 
                                                0.0273623182                                                -0.0021535087 
                                          canopycover_grad_4                                                 water_grad_1 
                                               -0.0274083168                                                 0.0317373349 
                                                water_grad_2                                                 water_grad_3 
                                               -0.2549622896                                                 0.0000000000 
                                                water_grad_4                                                        crw_1 
                                                0.0931048160                                                 0.9291095523 
                                                       crw_2                                                        crw_3 
                                                0.7597740210                                                -0.0929531749 
                                                       crw_4                                              temperature_loc 
                                                0.6372800436                                                 0.0000000000 
                                           precipitation_loc                            temperature_loc:precipitation_loc 
                                                0.0000000000                                                 0.0000000000 
# Build a monthly temperature and precipitation predictor
monthtemp = dat[, list(stemp=scale2(temperature_loc), monthofyear)][, list(temperature_loc=mean(stemp)), by=monthofyear]
monthprecip = dat[, list(sprecip=scale2(precipitation_loc), monthofyear)][, list(precipitation_loc=mean(sprecip)), by=monthofyear]
monthtp = cbind(monthtemp, monthprecip[, list(precipitation_loc)])
monthtp[, ("temperature_loc:precipitation_loc"):=temperature_loc*precipitation_loc]
splinehour = s(hourofday, bs="cc", k=df_hour)
pred_hours = Predict.matrix(smooth.construct2(splinehour, tdat, NULL), data.frame(hourofday=sort(unique(tdat$hourofday)))) # B-spline basis for hour effect
dailyres = list()
inmonth = unique(tdat$monthofyear)
plist = c("ndvi_grad_*", "masting_grad_*", "fruit_and_nutsdists_grad_*", "water_grad_*", "crw_*", "base_hour_*")
tpcols = c("temperature_loc", "precipitation_loc", "temperature_loc:precipitation_loc")
#plist = c("canopycover_loc_hour_*")
for(pattern in plist) { # Loop through each pattern
  
  dailyres[[pattern]] = list()
  tpbetas = bestbetas[names(bestbetas) %like% pattern]
  
  for(month in inmonth){
      
      basenames = paste0(strsplit(pattern, "*", fixed=T)[[1]], 1:(df_hour - 1))
      baseeff = pred_hours %*% t(t(tpbetas[basenames]))
      
      for(tp in tpcols){
        
        tnames = paste0(strsplit(pattern, "*", fixed=T)[[1]], 1:(df_hour - 1), ":", tp)
        tpval = as.numeric(monthtp[monthofyear == month, tp, with=F])
        teff = (pred_hours*tpval) %*% t(t(tpbetas[tnames]))
        
        baseeff = baseeff + teff
      }
        
      effdt = data.frame(hour=0:23, beta=baseeff, coef=pattern)
      effdt$monthofyear = month
      dailyres[[pattern]][[month]] = effdt
  }
}
monthres = do.call(rbind, lapply(dailyres, function(x) do.call(rbind, x)))
tplot = ggplot(monthres) + geom_line(aes(x=hour, y=beta, color=as.factor(monthofyear))) + xlab("Hour of day") + ylab("Effect on pig movement") + theme_bw() + facet_wrap(~coef, scales="free")
tpplot = ggplot(monthtp) + geom_line(aes(x=monthofyear, y=temperature_loc, color="temperature")) + 
                           geom_line(aes(x=monthofyear, y=precipitation_loc, color="precipitation")) + theme_bw() + xlab("Month of year") + ylab("Standardized mean monthy value")
require(gridExtra)
grid.arrange(tplot, tpplot, nrow=1, ncol=2)

Plot how the non-daily varying location-based variables change with temperature

basenames = sapply(strsplit(names(bestbetas), ":", fixed=T), function(x) x[[1]][1])
locparams = bestbetas[basenames %like% "*_loc*"]
seas_lps = c("ndvi_loc", "masting_loc") 
loceffects = list()
for(param in seas_lps){
  
  loceffects[[param]] = list()
  
  for(month in inmonth){
    
    baseeff = locparams[param]
    
    for(tp in tpcols){
      
      tpval = as.numeric(monthtp[monthofyear == month, tp, with=F])
      baseeff = baseeff + tpval*locparams[paste0(param, ":", tp)]
      
    }
    
    loceffects[[param]][[month]] = data.frame(month=month, coef=param, beta=baseeff)
  }
  
}
monthloc = do.call(rbind, lapply(loceffects, function(x) do.call(rbind, x)))
tplot = ggplot(monthloc) + geom_path(aes(x=month, y=beta)) + xlab("Month of year") + ylab("Effect on pig movement") + theme_bw() + facet_wrap(~coef)
#tplot
tpplot = ggplot(monthtp) + geom_line(aes(x=monthofyear, y=temperature_loc, color="temperature")) + 
                           geom_line(aes(x=monthofyear, y=precipitation_loc, color="precipitation")) + theme_bw() + xlab("Month of year") + ylab("Standardized mean monthy value")
require(gridExtra)
grid.arrange(tplot, tpplot, nrow=1, ncol=2)

Analysis on all pigs


alltpres = list()
alltpbestbetas = list()

for(pig in unique(dat$pigID)){
  
  cat("Working on", pig, "\n")
  tdat = dat[pigID == pig]
  tdat[ , datetime:=as.POSIXct(t, origin = '1970-01-01', tz = 'GMT')]
  tdat[ , hourofday:=scale(hour(datetime))]
  tdat[ , monthofyear:=month(datetime)]
  
  # Set fngrad to 0 if it is NA
  if(all(is.na(tdat$fruit_and_nutsdists_grad)))
     fngrad = rep(0, nrow(tdat))
  else
    fngrad = tdat$fruit_and_nutsdists_grad
  
  tdat$fruit_and_nutsdists_grad = fngrad
  
  df_hour = 5
  stdcols = c("ndvi_loc", "ndvi_grad", "masting_loc",
              "masting_grad", "water_grad",
              "elevation_loc", "elevation_grad",
              "fruit_and_nutsdists_grad",
              "canopycover_loc", "canopycover_grad", "crw")
  nonstdcols = c("fruit_and_nuts_loc")
  splinecols = c("ndvi_grad", "masting_grad", "elevation_grad", "fruit_and_nutsdists_grad",
                 "canopycover_grad", "water_grad", "crw")
  seasonalcols = c("fruit_and_nutsdists_grad", "masting_grad", "masting_loc", "ndvi_grad", "ndvi_loc", "water_grad",
                   "crw")
  Xseason = build_tempprecip_design_matrix(tdat, stdcols, nonstdcols, splinecols, seasonalcols, df_hour=df_hour)
  
  registerDoMC(cores=4)
  fitseason = cv.glmnet(Xseason, y=tdat$z, offset=log(tdat$tau), family="poisson", 
                        alpha=1, nlambda=20, nfolds=5, parallel=TRUE)
  
  bestbetas = fitseason$glmnet.fit$beta[, which(fitseason$lambda == fitseason$lambda.1se), drop=T]
  
  # Build a monthly temperature and precipitation predictor
  monthtemp = dat[, list(stemp=scale2(temperature_loc), monthofyear)][, list(temperature_loc=mean(stemp)), by=monthofyear]
  monthprecip = dat[, list(sprecip=scale2(precipitation_loc), monthofyear)][, list(precipitation_loc=mean(sprecip)), by=monthofyear]
  monthtp = cbind(monthtemp, monthprecip[, list(precipitation_loc)])
  monthtp[, ("temperature_loc:precipitation_loc"):=temperature_loc*precipitation_loc]
  
  splinehour = s(hourofday, bs="cc", k=df_hour)
  pred_hours = Predict.matrix(smooth.construct2(splinehour, tdat, NULL), data.frame(hourofday=sort(unique(tdat$hourofday)))) # B-spline basis for hour effect
  
  dailyres = list()
  inmonth = unique(tdat$monthofyear)
  
  plist = c("ndvi_grad_*", "masting_grad_*", "fruit_and_nutsdists_grad_*", "water_grad_*", "crw_*", "base_hour_*")
  tpcols = c("temperature_loc", "precipitation_loc", "temperature_loc:precipitation_loc")
  #plist = c("canopycover_loc_hour_*")
  
  for(pattern in plist) { # Loop through each pattern
    
    dailyres[[pattern]] = list()
    tpbetas = bestbetas[names(bestbetas) %like% pattern]
    
    for(month in inmonth){
      
      basenames = paste0(strsplit(pattern, "*", fixed=T)[[1]], 1:(df_hour - 1))
      baseeff = pred_hours %*% t(t(tpbetas[basenames]))
      
      for(tp in tpcols){
        
        tnames = paste0(strsplit(pattern, "*", fixed=T)[[1]], 1:(df_hour - 1), ":", tp)
        tpval = as.numeric(monthtp[monthofyear == month, tp, with=F])
        teff = (pred_hours*tpval) %*% t(t(tpbetas[tnames]))
        
        baseeff = baseeff + teff
      }
      
      effdt = data.frame(hour=0:23, beta=baseeff, coef=pattern)
      effdt$monthofyear = month
      dailyres[[pattern]][[month]] = effdt
    }
  }
  
  monthres = do.call(rbind, lapply(dailyres, function(x) do.call(rbind, x)))
  monthres$pigID = pig
  alltpres[[pig]] = monthres
  alltpbestbetas[[pig]] = bestbetas
  
}
allpigstp = as.data.table(do.call(rbind, alltpres))
meanpig = allpigstp[, list(beta=mean(beta)), by=list(monthofyear, hour, coef)]
meanpig$pigID = "meantejonpig"
meanpig = meanpig[, colnames(allpigstp), with=F]
allpigstpfull = rbind(allpigstp, meanpig)
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")
tplot = ggplot(allpigstpfull) + geom_line(aes(x=hour, y=beta, color=as.factor(monthofyear))) + xlab("Hour of day") + ylab("Effect on pig movement") + theme_bw() + facet_wrap(~pigID + coef, scales="free", ncol=6, nrow=14) #+ scale_colour_brewer(palette = "Spectral")
tplot

Plot the non-daily coefficients

allpigloceffects = list()
for(pig in names(alltpbestbetas)){
  
  inmonth = unique(dat[pigID == pig]$monthofyear)
  bestbetas = alltpbestbetas[[pig]]
  basenames = sapply(strsplit(names(bestbetas), ":", fixed=T), function(x) x[[1]][1])
  locparams = bestbetas[basenames %like% "*_loc*"]
  
  seas_lps = c("ndvi_loc", "masting_loc") 
  
  loceffects = list()
  for(param in seas_lps){
    
    loceffects[[param]] = list()
    
    for(month in inmonth){
      
      baseeff = locparams[param]
      
      for(tp in tpcols){
        
        tpval = as.numeric(monthtp[monthofyear == month, tp, with=F])
        baseeff = baseeff + tpval*locparams[paste0(param, ":", tp)]
        
      }
      
      loceffects[[param]][[month]] = data.frame(month=month, coef=param, beta=baseeff)
    }
    
  }
  
  monthloc = do.call(rbind, lapply(loceffects, function(x) do.call(rbind, x)))
  monthloc$pigID = pig
  allpigloceffects[[pig]] = monthloc
}
loccoefs = as.data.table(do.call(rbind, allpigloceffects))
mloccoefs = loccoefs[, list(beta=median(beta)), by=list(coef, month) ]
tplot = ggplot(loccoefs) + geom_point(aes(x=month, y=beta)) + geom_smooth(aes(x=month, y=beta), stat="smooth") + xlab("Month of year") + ylab("Effect on pig movement") + theme_bw() + facet_wrap(~coef)
tplot

tpplot = ggplot(monthtp) + geom_line(aes(x=monthofyear, y=temperature_loc, color="temperature")) + 
                           geom_line(aes(x=monthofyear, y=precipitation_loc, color="precipitation")) + theme_bw() + xlab("Month of year") + ylab("Standardized mean monthy value")
require(gridExtra)
grid.arrange(tplot, tpplot, nrow=1, ncol=2)

Wow, really hard to see much consistency.

LS0tCnRpdGxlOiAiVGVqb24gYW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClRyeWluZyB2YWxpZGF0ZSBtb2RlbCBhbmQgY292YXJpYXRlcyB3aXRoIGEgc2luZ2xlIHBpZyBhbmQgdGhlbiBzY2FsaW5nIHVwIHRvIGFsbCBwaWdzIGF0IFRlam9uIFJhbmNoLgoKYGBge3J9CmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShyYXN0ZXIpCmxpYnJhcnkoeWFtbCkKbGlicmFyeShnbG1uZXQpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgpMb2FkIGluIHJhc3RlcnMgYW5kIG1vdmVtZW50IG1vZGVsCgpgYGB7cn0KYW5hbF9wYXJhbXMgPSB5YW1sLmxvYWRfZmlsZSgiYW5hbHlzaXNfcGFyYW1ldGVycy55bWwiKQpkYXQgPSBmcmVhZCgifi9SZXBvcy9yc2Zfc3dpbmUvcmVzdWx0cy9nbG1kYXRhX2J5X3N0dWR5L3Rlam9uLmNzdiIpCmRhdFsgLCBkYXRldGltZTo9YXMuUE9TSVhjdCh0LCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gJ0dNVCcpXQpkYXRbICwgaG91cm9mZGF5Oj1zY2FsZShob3VyKGRhdGV0aW1lKSldCmRhdFsgLCBtb250aG9meWVhcjo9bW9udGgoZGF0ZXRpbWUpXQoKIyByYXdkYXQgPSBmcmVhZCgifi9SZXBvcy9yc2Zfc3dpbmUvZGF0YS9mb3JtYXR0ZWQvZnVsbF9waWdfZGF0YS5jc3YiKQpyYXMgPSByYXN0ZXIoIn4vUmVwb3MvcnNmX3N3aW5lL2RhdGEvY292YXJpYXRlX2RhdGEvY3JvcGxheWVyL3Rlam9uL3Rlam9uX2ZydWl0X2FuZF9udXRzXzIwMTUudGlmIikKY3JhcyA9IHJhc3Rlcigifi9SZXBvcy9yc2Zfc3dpbmUvZGF0YS9jb3ZhcmlhdGVfZGF0YS9jcm9wbGF5ZXIvdGVqb24vdGVqb25fY2VyZWFsc18yMDE2LnRpZiIpCnJhc3dhdGVyID0gcmFzdGVyKCJ+L1JlcG9zL3JzZl9zd2luZS9kYXRhL2NvdmFyaWF0ZV9kYXRhL3dhdGVyL3Rlam9uL3Rlam9uX3dhdGVyX25uZGlzdGFuY2UudGlmIikKcmFzZWwgPSByYXN0ZXIoIn4vUmVwb3MvcnNmX3N3aW5lL2RhdGEvY292YXJpYXRlX2RhdGEvZWxldmF0aW9uL3Rlam9uL3Rlam9uX2VsZXZhdGlvbi50aWYiKQpyYXNuZHZpID0gcmFzdGVyKCJ+L1JlcG9zL3JzZl9zd2luZS9kYXRhL2NvdmFyaWF0ZV9kYXRhL25kdmkvdGVqb24vdGVqb25fbmR2aV80XzIwMTYudGlmIikKcmFzbWFzdCA9IHJhc3Rlcigifi9SZXBvcy9yc2Zfc3dpbmUvZGF0YS9jb3ZhcmlhdGVfZGF0YS9tYXN0aW5nL3Rlam9uL3Rlam9uX21hc3RpbmcudGlmIikKcmFzY292ZXIgPSByYXN0ZXIoIn4vUmVwb3MvcnNmX3N3aW5lL2RhdGEvY292YXJpYXRlX2RhdGEvbGFuZGNvdmVyLy90ZWpvbi90ZWpvbl9jYW5vcHljb3Zlci50aWYiKQpzaHBjcm9wID0gc2hhcGVmaWxlKCJ+L1JlcG9zL3JzZl9zd2luZS9kYXRhL2NvdmFyaWF0ZV9kYXRhL2Nyb3BsYXllci90ZWpvbi90ZWpvbl9mcnVpdF9hbmRfbnV0c18yMDE1LnNocCIpCmBgYAoKYGBge3J9CmNzaHAgPSBzaGFwZWZpbGUoIn4vUmVwb3MvcnNmX3N3aW5lL2RhdGEvY292YXJpYXRlX2RhdGEvd2F0ZXIvdGVqb24vcmF3L3Rlam9uX3dhdGVyX253aV9yYXcuc2hwIikKY3NocF90cmFucyA9IHNwVHJhbnNmb3JtKGNzaHAsIENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiKSkKCiMgT25seSBjb25zaWRlciBwZXJtYW5lbnQgYW5kIHNlbWktcGVybWFuZW50IHdhdGVyIHNvdXJjZXM6ICJIIiBhbmQgIkYiIGluIE5XSSBsaW5nbzogaHR0cHM6Ly93d3cuZndzLmdvdi93ZXRsYW5kcy9EYXRhL1dldGxhbmQtQ29kZXMuaHRtbApwZXJlbiA9IGNzaHBfdHJhbnNbZ3JlcGwoIkgiLCBjc2hwX3RyYW5zJEFUVFJJQlVURSkgfCBncmVwbCgiRiIsIGNzaHBfdHJhbnMkQVRUUklCVVRFKSwgXQpgYGAKCmBgYHtyfQp1bmlxdWUoZGF0JHBpZ0lEKQpgYGAKClZpc3VhbGl6ZSB0aGUgcGlnIG1vdmVtZW50IApgYGB7cn0KdGRhdCA9IGRhdFtwaWdJRCA9PSAidGVqb25NMzAyIl0KdGRhdFsgLCBkYXRldGltZTo9YXMuUE9TSVhjdCh0LCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gJ0dNVCcpXQoKcGxvdChjcm9wKHJhc2NvdmVyLCByYXMpKQojcGxvdChyYXMsIGNvbD1jKCJ3aGl0ZSIsICJibHVlIiksIGFkZD1UKQpwbG90KHNocGNyb3AsIGFkZD1UKQpwbG90KHBlcmVuLCBhZGQ9VCwgYm9yZGVyPSdibHVlJykKcG9pbnRzKHRkYXRbLCB4LmFkal0sIHRkYXRbLCB5LmFkal0sIGNleD0wLjAxLCBjb2w9InJlZCIsIHBjaD0xOSkKcG9pbnRzKHRkYXRbY3JvcF9sb2MgPT0gMSwgeC5hZGpdLCB0ZGF0W2Nyb3BfbG9jID09IDEsIHkuYWRqXSwgY2V4PTAuMSwgY29sPSJncmF5IiwgcGNoPTE5KQoKCiNwb2ludHModGRhdFssIHguYWRqXSwgdGRhdFssIHkuYWRqXSwgY2V4PTAuMDEsIGNvbD0icmVkIiwgcGNoPTE5KQojcG9pbnRzKHRkYXRbY3JvcF9sb2MgPT0gMSwgeC5hZGpdLCB0ZGF0W2Nyb3BfbG9jID09IDEsIHkuYWRqXSwgY2V4PTAuMDUsIGNvbD0iYmx1ZSIsIHBjaD0xOSkKI3Bsb3Qoc3AsIGFkZD1UKQpgYGAKCiMjIEZpdCBhIHNpbmdsZSwgY3JvcC11c2luZyBwaWcgCgpGaXJzdCBqdXN0IGxvb2sgYXQgdGhlIGVmZmVjdHMgb2YgbW92ZW1lbnQgaW5kZXBlbmRlbnQgb2YgaG91cgoKYGBge3J9CnJlcXVpcmUoZG9NQykKc291cmNlKCJwaWdmeG5zLlIiKQoKdGRhdCA9IGRhdFtwaWdJRCA9PSAidGVqb25NMzAyIl0KdGRhdFsgLCBkYXRldGltZTo9YXMuUE9TSVhjdCh0LCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gJ0dNVCcpXQoKWGZ1bGwgPSBidWlsZF9kZXNpZ25fbWF0cml4Mih0ZGF0LCBjKCJuZHZpX2xvYyIsICJuZHZpX2dyYWQiLCAibWFzdGluZ19sb2MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWFzdGluZ19ncmFkIiwgIndhdGVyX2dyYWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZWxldmF0aW9uX2xvYyIsICJlbGV2YXRpb25fZ3JhZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2Fub3B5Y292ZXJfbG9jIiwgImNhbm9weWNvdmVyX2dyYWQiLCAiY3J3IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJmcnVpdF9hbmRfbnV0c19sb2MiKSkKCnJlZ2lzdGVyRG9NQyhjb3Jlcz00KQoKdGZpdCA9IGN2LmdsbW5ldChYZnVsbCwgeT10ZGF0JHosIG9mZnNldD1sb2codGRhdCR0YXUpLCAKICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHk9InBvaXNzb24iLCAKICAgICAgICAgICAgICAgICAgICAgICBhbHBoYT0xLCBubGFtYmRhPTIwLCBuZm9sZHM9NCwgcGFyYWxsZWw9VFJVRSkKYGBgCgpgYGB7cn0KdGZpdCRnbG1uZXQuZml0JGJldGFbLCB3aGljaCh0Zml0JGxhbWJkYSA9PSB0Zml0JGxhbWJkYS5taW4pLCBkcm9wPUZdCmBgYAoKU3Vic3RhbnRpYWxseSBzbG93ZXIgbW92ZW1lbnQgb25jZSB0aGUgcGlnIGlzIGluIGEgY3JvcCBmaWVsZC4gQW4gb3ZlcmFsbCB0ZW5kZW5jeSB0byBtb3ZlIHRvd2FyZCBjcm9wIGZpZWxkcyAoTk9URTogdGhpcyBpcyBoaWdobHkgZGVwZW5kZW50IG9uIHRoZSB0aW1lIG9mIGRheSEpLiAgQSB0ZW5kZW5jeSB0byBzdGF5IGxvbmdlciBpbiBoaWdoZXIgY292ZXIgCgpGaXQgYSBzdGFuIG1vZGVsIHRvIHRoaXMgZGF0YQoKYGBge3J9CmxpYnJhcnkocnN0YW4pCgpwb2lzZ2xtID0gc3Rhbl9tb2RlbCgic3Rhbl9maWxlcy9wb2lzc29uX2dsbS5zdGFuIikKYGBgCgpgYGB7cn0KWGZ1bGxzdGFuID0gY2JpbmQoMSwgWGZ1bGwpCnNkYXRhID0gbGlzdChYPVhmdWxsc3RhbiwgTj1ucm93KFhmdWxsc3RhbiksIHA9bmNvbChYZnVsbHN0YW4pLCB6PXRkYXQkeiwgdGF1PXRkYXQkdGF1KQpmaXRwZ2xtID0gc2FtcGxpbmcocG9pc2dsbSwgZGF0YT1zZGF0YSwgY2hhaW5zPTMsIGl0ZXI9MjAwMCkKYGBgCgpMZXQncyBhZGQgaW4gZGFpbHkgdmFyaWF0aW9uIGluIG1vdmVtZW50IGJlaGF2aW9yLgoKYGBge3J9CnRkYXRbICwgZGF0ZXRpbWU6PWFzLlBPU0lYY3QodCwgb3JpZ2luID0gJzE5NzAtMDEtMDEnLCB0eiA9ICdHTVQnKV0KdGRhdFsgLCBob3Vyb2ZkYXk6PXNjYWxlKGhvdXIoZGF0ZXRpbWUpKV0KCmRmX2hvdXIgPSA1CnN0ZGNvbHMgPSBjKCJuZHZpX2xvYyIsICJuZHZpX2dyYWQiLCAibWFzdGluZ19sb2MiLAogICAgICAgICAgICAibWFzdGluZ19ncmFkIiwgIndhdGVyX2dyYWQiLAogICAgICAgICAgICAiZWxldmF0aW9uX2xvYyIsICJlbGV2YXRpb25fZ3JhZCIsCiAgICAgICAgICAgICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQiLAogICAgICAgICAgICAiY2Fub3B5Y292ZXJfbG9jIiwgImNhbm9weWNvdmVyX2dyYWQiLCAiY3J3IikKbm9uc3RkY29scyA9IGMoImZydWl0X2FuZF9udXRzX2xvYyIpCnNwbGluZWNvbHMgPSBjKCJuZHZpX2dyYWQiLCAibWFzdGluZ19ncmFkIiwgImVsZXZhdGlvbl9ncmFkIiwgImZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCIsCiAgICAgICAgICAgICAgICAiY2Fub3B5Y292ZXJfZ3JhZCIsICJ3YXRlcl9ncmFkIiwgImNydyIpClhnYW0gPSBidWlsZF9kYWlseV9zcGxpbmVfZGVzaWduX21hdHJpeCh0ZGF0LCBzdGRjb2xzLCBub25zdGRjb2xzLCBzcGxpbmVjb2xzLCBkZl9ob3VyPWRmX2hvdXIpCgpyZWdpc3RlckRvTUMoY29yZXM9NCkKZml0Z2FtID0gY3YuZ2xtbmV0KFhnYW0sIHk9dGRhdCR6LCBvZmZzZXQ9bG9nKHRkYXQkdGF1KSwgZmFtaWx5PSJwb2lzc29uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgYWxwaGE9MSwgbmxhbWJkYT0yMCwgbmZvbGRzPTUsIHBhcmFsbGVsPVRSVUUpCmBgYAoKYGBge3J9CmJlc3RiZXRhcyA9IGZpdGdhbSRnbG1uZXQuZml0JGJldGFbLCB3aGljaChmaXRnYW0kbGFtYmRhID09IGZpdGdhbSRsYW1iZGEubWluKSwgZHJvcD1UXQpiZXN0YmV0YXMKYGBgCgpQbG90IHRoZSBkYWlseSBlZmZlY3RzCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpzcGxpbmVob3VyID0gcyhob3Vyb2ZkYXksIGJzPSJjYyIsIGs9ZGZfaG91cikKcHJlZF9ob3VycyA9IFByZWRpY3QubWF0cml4KHNtb290aC5jb25zdHJ1Y3QyKHNwbGluZWhvdXIsIHRkYXQsIE5VTEwpLCBkYXRhLmZyYW1lKGhvdXJvZmRheT1zb3J0KHVuaXF1ZSh0ZGF0JGhvdXJvZmRheSkpKSkgIyBCLXNwbGluZSBiYXNpcyBmb3IgaG91ciBlZmZlY3QKCmRhaWx5cmVzID0gbGlzdCgpCnBsaXN0ID0gYygiYmFzZV9ob3VyXyoiLCAibmR2aV9ncmFkXyoiLCAibWFzdGluZ19ncmFkXyoiLCAiZWxldmF0aW9uX2dyYWRfKiIsICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWRfKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2Fub3B5Y292ZXJfZ3JhZF8qIiwgIndhdGVyX2dyYWRfKiIsICJjcndfKiIpCiNwbGlzdCA9IGMoImNhbm9weWNvdmVyX2xvY19ob3VyXyoiKQpmb3IocGF0dGVybiBpbiBwbGlzdCkgewogIAogIGVmZiA9IHByZWRfaG91cnMgJSolIHQodChiZXN0YmV0YXNbbmFtZXMoYmVzdGJldGFzKSAlbGlrZSUgcGF0dGVybl0pKQogIGVmZmR0ID0gZGF0YS5mcmFtZShob3VyPTA6MjMsIGJldGE9ZWZmLCBjb2VmPXBhdHRlcm4pCiAgCiAgZGFpbHlyZXNbW3BhdHRlcm5dXSA9IGVmZmR0Cn0KCmRhaWx5cmVzID0gZG8uY2FsbChyYmluZCwgZGFpbHlyZXMpCnRwbG90ID0gZ2dwbG90KGRhaWx5cmVzKSArIGdlb21fbGluZShhZXMoeD1ob3VyLCB5PWJldGEpKSArIHhsYWIoIkhvdXIgb2YgZGF5IikgKyB5bGFiKCJFZmZlY3Qgb2Ygb24gcGlnIG1vdmVtZW50IikgKyB0aGVtZV9idygpICsgZmFjZXRfd3JhcCh+Y29lZiwgc2NhbGVzPSJmcmVlIikKcHJpbnQodHBsb3QpCiNnZ3NhdmUoIi4uL2RvY3MvcHJlc2VudGF0aW9ucy9pbWFnZXMvY2Fub3B5Y292ZXJsb2MucGRmIiwgd2lkdGg9NSwgaGVpZ2h0ID0zKQoKYGBgCgoKVGhpcyBpcyBleGFjdGx5IHdoYXQgd2Ugd291bGQgZXhwZWN0IGJhc2VkIG9uIGEgZW1waXJpY2FsIGV4cGxvcmF0aW9uIG9mIHRoZSBtb3ZlbWVudCBwYXR0ZXJucy4gIEEgc3Ryb25nIHRlbmRlbmN5IHRvIG1vdmUgdG93YXJkIHRoZSB1c2VkIGZydWl0IGFuZCBudXQgZmllbGRzIGR1cmluZyB0aGUgZXZlbmluZyBob3VycyBhbmQgdGhpIHRlbmRlbmN5IHJldmVyc2VzIGluIHRoZSBpbiB0aGUgZWFybHkgbW9ybmluZyBob3VycyB3aGVyZSB0aGUgcGlnIGlzIG1vdmluZyBhd2F5IGZyb20gdGhlc2UgZmllbGRzLiBCZWNhdXNlIHRoZSBmaWVsZHMgYXJlIGluIGxvdyBORFZJIGFyZWFzLCB0aGVyZSBpcyBhIHN0cm9uZyBjb3JyZWxhdGlvbiAKCi0tLQoKRml0IG1vZGVsIHRvIGFsbCBwaWdzLgoKYGBge3J9CnVuaXF1ZShkYXQkcGlnSUQpCmBgYAoKYGBge3J9CgphbGxyZXMgPSBsaXN0KCkKbm9uZ2FtID0gbGlzdCgpCmFsbGJlc3RiZXRhcyA9IGxpc3QoKQoKZm9yKHBpZyBpbiB1bmlxdWUoZGF0JHBpZ0lEKSl7CiAgCiAgIyBFeHRyYWN0IGEgZm9ybWF0IGRhdGEKICB0ZGF0ID0gZGF0W3BpZ0lEID09IHBpZ10KICBjYXQoIkZpdHRpbmcgcGlnIiwgcGlnLCAiXG4iKQogIHRkYXRbICwgZGF0ZXRpbWU6PWFzLlBPU0lYY3QodCwgb3JpZ2luID0gJzE5NzAtMDEtMDEnLCB0eiA9ICdHTVQnKV0KICB0ZGF0WyAsIGhvdXJvZmRheTo9c2NhbGUoaG91cihkYXRldGltZSkpXQogIAogICMgU2V0IGZuZ3JhZCB0byAwIGlmIGl0IGlzIE5BCiAgaWYoYWxsKGlzLm5hKHRkYXQkZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkKSkpCiAgICAgZm5ncmFkID0gcmVwKDAsIG5yb3codGRhdCkpCiAgZWxzZQogICAgZm5ncmFkID0gdGRhdCRmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQKICAKICB0ZGF0JGZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCA9IGZuZ3JhZAoKICAjIFN0ZXAgMTogRml0IHRoZSBub24tZGFpbHkgbW9kZWwKICBYZnVsbCA9IGJ1aWxkX2Rlc2lnbl9tYXRyaXgyKHRkYXQsIGMoIm5kdmlfbG9jIiwgIm5kdmlfZ3JhZCIsICJtYXN0aW5nX2xvYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtYXN0aW5nX2dyYWQiLCAid2F0ZXJfZ3JhZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbGV2YXRpb25fbG9jIiwgImVsZXZhdGlvbl9ncmFkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYW5vcHljb3Zlcl9sb2MiLCAiY2Fub3B5Y292ZXJfZ3JhZCIsICJjcnciKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImZydWl0X2FuZF9udXRzX2xvYyIpKQoKICByZWdpc3RlckRvTUMoY29yZXM9NCkKICB0Zml0ID0gY3YuZ2xtbmV0KFhmdWxsLCB5PXRkYXQkeiwgb2Zmc2V0PWxvZyh0ZGF0JHRhdSksIAogICAgICAgICAgICAgICAgICAgICAgIGZhbWlseT0icG9pc3NvbiIsIAogICAgICAgICAgICAgICAgICAgICAgIGFscGhhPTEsIG5sYW1iZGE9MjAsIG5mb2xkcz00LCBwYXJhbGxlbD1UUlVFKQogIAogIG5vbmdhbVtbcGlnXV0gPSB0Zml0JGdsbW5ldC5maXQkYmV0YVssIHdoaWNoKHRmaXQkbGFtYmRhID09IHRmaXQkbGFtYmRhLm1pbiksIGRyb3A9VF0KICAKICAjIFN0ZXAgMjogRml0IHRoZSBkYWlseSBtb2RlbAogIHN0ZGNvbHMgPSBjKCJuZHZpX2xvYyIsICJuZHZpX2dyYWQiLCAibWFzdGluZ19sb2MiLAogICAgICAgICAgICAgICJtYXN0aW5nX2dyYWQiLCAid2F0ZXJfZ3JhZCIsCiAgICAgICAgICAgICAgImVsZXZhdGlvbl9sb2MiLCAiZWxldmF0aW9uX2dyYWQiLAogICAgICAgICAgICAgICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQiLAogICAgICAgICAgICAgICJjYW5vcHljb3Zlcl9sb2MiLCAiY2Fub3B5Y292ZXJfZ3JhZCIsICJjcnciKQogIG5vbnN0ZGNvbHMgPSBjKCJmcnVpdF9hbmRfbnV0c19sb2MiKQogIHNwbGluZWNvbHMgPSBjKCJuZHZpX2dyYWQiLCAibWFzdGluZ19ncmFkIiwgImVsZXZhdGlvbl9ncmFkIiwgImZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCIsCiAgICAgICAgICAgICAgICAgImNhbm9weWNvdmVyX2dyYWQiLCAid2F0ZXJfZ3JhZCIsICJjcnciKQogIAogIGlmKHBpZyA9PSAidGVqb25NMzAyIikKICAgIFh0ZW1wID0gWGdhbQogIAogIFhnYW0gPSBidWlsZF9kYWlseV9zcGxpbmVfZGVzaWduX21hdHJpeCh0ZGF0LCBzdGRjb2xzLCBub25zdGRjb2xzLCBzcGxpbmVjb2xzLCBkZl9ob3VyPTUpCiAgCiAgcmVnaXN0ZXJEb01DKGNvcmVzPTQpCiAgZml0Z2FtID0gY3YuZ2xtbmV0KFhnYW0sIHk9dGRhdCR6LCBvZmZzZXQ9bG9nKHRkYXQkdGF1KSwgZmFtaWx5PSJwb2lzc29uIiwgCiAgICAgICAgICAgICAgICAgICAgIGFscGhhPTEsIG5sYW1iZGE9MjAsIG5mb2xkcz01LCBwYXJhbGxlbD1UUlVFKQogIAogIGJlc3RiZXRhcyA9IGZpdGdhbSRnbG1uZXQuZml0JGJldGFbLCB3aGljaChmaXRnYW0kbGFtYmRhID09IGZpdGdhbSRsYW1iZGEuMXNlKSwgZHJvcD1UXQogIGFsbGJlc3RiZXRhc1tbcGlnXV0gPSBiZXN0YmV0YXMKICAKICAjIFN0ZXAgMzogRXh0cmFjdCBjeWNsaWMgYmFzaXMgZm9yIGhvdXIgZWZmZWN0CiAgc3BsaW5laG91ciA9IHMoaG91cm9mZGF5LCBicz0iY2MiLCBrPWRmX2hvdXIpCiAgcHJlZF9ob3VycyA9IFByZWRpY3QubWF0cml4KHNtb290aC5jb25zdHJ1Y3QyKHNwbGluZWhvdXIsIHRkYXQsIE5VTEwpLCBkYXRhLmZyYW1lKGhvdXJvZmRheT1zb3J0KHVuaXF1ZSh0ZGF0JGhvdXJvZmRheSkpKSkKICAKICBkYWlseXJlcyA9IGxpc3QoKQogIHBsaXN0ID0gYygiYmFzZV9ob3VyXyoiLCAibmR2aV9ncmFkXyoiLCAibWFzdGluZ19ncmFkXyoiLCAiZWxldmF0aW9uX2dyYWRfKiIsICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWRfKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2Fub3B5Y292ZXJfZ3JhZF8qIiwgIndhdGVyX2dyYWRfKiIsICJjcndfKiIpCiAgZm9yKHBhdHRlcm4gaW4gcGxpc3QpIHsKICAgIAogICAgZWZmID0gcHJlZF9ob3VycyAlKiUgdCh0KGJlc3RiZXRhc1tuYW1lcyhiZXN0YmV0YXMpICVsaWtlJSBwYXR0ZXJuXSkpCiAgICBlZmZkdCA9IGRhdGEuZnJhbWUoaG91cj0wOjIzLCBiZXRhPWVmZiwgY29lZj1wYXR0ZXJuKQogICAgZGFpbHlyZXNbW3BhdHRlcm5dXSA9IGVmZmR0CiAgfQogIAogIGRhaWx5cmVzID0gZG8uY2FsbChyYmluZCwgZGFpbHlyZXMpCiAgZGFpbHlyZXMkcGlnSUQgPSBwaWcKICBjYXQoIlNhdmluZyIsIHBpZywgIlxuIikKICBhbGxyZXNbW3BpZ11dID0gZGFpbHlyZXMKICAKfQpgYGAKCmBgYHtyfQphbGxyZXNfZHQgPSBhcy5kYXRhLnRhYmxlKGRvLmNhbGwocmJpbmQsIGFsbHJlcykpCmdncGxvdChhbGxyZXNfZHQpICsgZ2VvbV9saW5lKGFlcyh4PWhvdXIsIHk9YmV0YSwgY29sb3I9cGlnSUQpKSArIHhsYWIoIkhvdXIgb2YgZGF5IikgKyB5bGFiKCJFZmZlY3Qgb24gaG91cmx5IG1vdmVtZW50IHJhdGUiKSArIHRoZW1lX2J3KCkgKyBmYWNldF93cmFwKH5jb2VmLCBzY2FsZXM9ImZyZWUiKQpgYGAKCmBgYHtyfQpsb2NwYXJhbXMgPSBzYXBwbHkoYWxsYmVzdGJldGFzLCBmdW5jdGlvbih4KSB4W25hbWVzKHgpICVsaWtlJSAiKl9sb2MiXSkKbWxvY3BhcmFtcyA9IG1lbHQobG9jcGFyYW1zKQpjb2xuYW1lcyhtbG9jcGFyYW1zKSA9IGMoImNvZWYiLCAicGlnIiwgInZhbHVlIikKZ2dwbG90KG1sb2NwYXJhbXMpICsgZ2VvbV9ib3hwbG90KGFlcyh4PWNvZWYsIHk9dmFsdWUpKSArIHRoZW1lX2J3KCkKYGBgCgpBIGNvbnNpc3RlbnQgdGVuZGVjeSB0byByZW1haW4gbG9uZ2VyIGluIGhpZ2hlciBORFZJIGNlbGxzLiAgTm8gY29uc2lzdGVudCBlZmZlY3Qgb2YgZWxldmF0aW9uIG9yIG1hc3RpbmcuIENvbnNpZGVyaW5nIGhvdyBzZWFzb25hbCBtYXN0aW5nIGlzLCB0aGlzIGlzIG5vdCBhbHRvZ2V0aGVyIHN1cnByaXNpbmcuIApDYW5vcHkgY292ZXIgaXMsIGEgYml0IG1vcmUgc3VycHJpc2luZ2x5IG11Y2ggbW9yZSB2YXJpYWJsZS4gCgpgYGB7cn0Kbm9uZ2FtcGFyYW1zID0gZG8uY2FsbChyYmluZCwgbm9uZ2FtKQptcGFyYW1zID0gbWVsdChub25nYW1wYXJhbXMpCmNvbG5hbWVzKG1wYXJhbXMpID0gYygicGlnIiwgImNvZWYiLCAidmFsdWUiKQoKZ2dwbG90KG1wYXJhbXMpICsgZ2VvbV9ib3hwbG90KGFlcyh4PWNvZWYsIHk9dmFsdWUpKSArIHRoZW1lX2J3KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQoKYGBgCgpUaGVyZSBhcmUgdHdvIHBpZ3MgdGhhdCBzZWVtIHRvIGJlIGRvaW5nIHNvbWUgd2VpcmQgdGhpbmdzOiB0ZWpvbkYwNyBhbmQgdGVqb25GMDUuICBMZXQncyBsb29rIGF0IHRoZXNlIGEgYml0IG1vcmUgY2xvc2VseS4KCmBgYHtyfQpyZXF1aXJlKGx1YnJpZGF0ZSkKdEYwNyA9IGRhdFtwaWdJRCA9PSAidGVqb25GMDUiXQp0RjA3WywgZGF0ZXRpbWU6PWFzLlBPU0lYY3QodCwgb3JpZ2luID0gJzE5NzAtMDEtMDEnLCB0eiA9ICdHTVQnKV0KCm1heCh0RjA3JGRhdGV0aW1lKSAtIG1pbih0RjA3JGRhdGV0aW1lKQpwbG90KHRGMDckZGF0ZXRpbWUsIHJlcCgxLCBucm93KHRGMDcpKSkKaGlzdChob3VyKHRGMDckZGF0ZXRpbWUpKQpgYGAKCkZvciB0aGVzZSBwaWdzLCBpdCBkb2VzIHNlZW0gdGhhdCB0aGV5IGFyZSBtb3ZpbmcgbXVjaCBtb3JlIGR1cmluZyB0aGUgZGF5IHRoYW4gb3RoZXIgcGlncyBpbiB0aGUgcG9wdWxhdGlvbi4gV2h5IGlzIHRoYXQ/IAoKIyMgRXhwbG9yZSBhIFBDQSBhbmFseXNpcyBvbiB0aGUgdmFyaWFibGVzIGZvciBkaWZmZXJlbnQgcGlncyB0byBsb29rIGZvciBzaW1pYXJsaXRpZXMgYW5kIGRpZmZlcmVuY2VzCgoKYGBge3J9CmxpYnJhcnkoZ2dmb3J0aWZ5KQpsaWJyYXJ5KGdncGxvdDIpCmFsbHBhcmFtcyA9IGRvLmNhbGwocmJpbmQsIGFsbGJlc3RiZXRhcykKCiMgRHJvcCBjb2x1bW5zIHRoYXQgYXJlIGFsbCAwCm5vdDAgPSAhYXBwbHkoYWxscGFyYW1zLCAyLCBmdW5jdGlvbih4KSBhbGwoeCA9PSAwKSkKcmVkcGFyYW1zID0gYWxscGFyYW1zWywgbm90MF0KCnJlZHBhcmFtc19kdCA9IGFzLmRhdGEuZnJhbWUocmVkcGFyYW1zKQpyZWRwYXJhbXNfZHQkcGlnSUQgPSByb3duYW1lcyhyZWRwYXJhbXMpCgpwY2FyZXMgPSBzdGF0czo6cHJjb21wKHJlZHBhcmFtcywgc2NhbGU9VCkKYXV0b3Bsb3QocGNhcmVzLCBkYXRhID0gcmVkcGFyYW1zX2R0LCBjb2xvdXIgPSAncGlnSUQnLAogICAgICAgICBsb2FkaW5ncyA9IFQsIGxvYWRpbmdzLmNvbG91ciA9ICdibHVlJywKICAgICAgICAgbG9hZGluZ3MubGFiZWwgPSBULCBsb2FkaW5ncy5sYWJlbC5zaXplID0gMiwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZT0zKQoKCiMocGNhcmVzJHNkZXYpXjIgLyBzdW0oKHBjYXJlcyRzZGV2KV4yKQojYmFycGxvdChwY2FyZXMkcm90YXRpb25bLCAyXVtvcmRlcihhYnMocGNhcmVzJHJvdGF0aW9uWywgMl0pKV0pCmBgYAoKYGBge3J9Cgpsb2NwYXJhbXMgPSByZWRwYXJhbXNbICwgY29sbmFtZXMocmVkcGFyYW1zKSAlbGlrZSUgIipfbG9jIl0KCmZvcihwIGluIHBsaXN0KXsKICBzdW1wID0gdCh0KHJvd1N1bXMocmVkcGFyYW1zWywgY29sbmFtZXMocmVkcGFyYW1zKSAlbGlrZSUgcF0pKSkKICBsb2NwYXJhbXMgPSBjYmluZChzdW1wLCBsb2NwYXJhbXMpCiAgY29sbmFtZXMobG9jcGFyYW1zKVsxXSA9IHAKfQoKbG9jcGFyYW1zID0gbG9jcGFyYW1zWywgIShjb2xuYW1lcyhsb2NwYXJhbXMpICVpbiUgYygiY3J3XyoiLCAiYmFzZV9ob3VyXyoiKSldCmxvY3BhcmFtc19kdCA9IGFzLmRhdGEuZnJhbWUobG9jcGFyYW1zKQpsb2NwYXJhbXNfZHQkcGlnSUQgPSByb3duYW1lcyhsb2NwYXJhbXMpCgpwbG9jID0gc3RhdHM6OnByY29tcChsb2NwYXJhbXMsIHNjYWxlPVQpCmF1dG9wbG90KHBsb2MsIGRhdGEgPSBsb2NwYXJhbXNfZHQsIGNvbG91ciA9ICdwaWdJRCcsCiAgICAgICAgIGxvYWRpbmdzID0gVCwgbG9hZGluZ3MuY29sb3VyID0gJ2JsdWUnLAogICAgICAgICBsb2FkaW5ncy5sYWJlbCA9IFQsIGxvYWRpbmdzLmxhYmVsLnNpemUgPSAyLCBsYWJlbD1UUlVFLCBsYWJlbC5zaXplPTMpCgpgYGAKCiMjIyBBZGQgaW4gc2Vhc29uYWwgZWZmZWN0cyBmb3IgVGVqb24gcGlncwoKV2UgaGF2ZSBjb25zaWRlcmVkIGRhaWx5IGVmZmVjdHMgb2YgbW92ZW1lbnQsIG5vIGxldCdzIGNvbnNpZGVyIHNlYXNvbmFsIChpLmUuIG1vbnRobHkgZWZmZWN0cyBvbiBtb3ZlbWVudCkuICBUd28gYXBwcm9hY2hlcyBmb3IgZG9pbmcgdGhpcwoKMS4gRml0IGEgbW9udGx5IHNwbGluZQoyLiBCcmVhayB0aGUgZGF0YSBpbnRvIDQgc2Vhc29ucyBhbmQgZml0IGEgInNlYXNvbiIgZmFjdG9yIHRoYXQgaW50ZXJhY3RzIHdpdGggZGFpbHkgZWZmZWN0cy4KCioqUHJlZGljdGVkIHNlYXNvbmFsIGNvdmFyaWF0ZXMqKgoKTGlrZWx5IHNlYXNvbmFsIGNvdmFyaWF0ZXMKCjEuIENyb3AgZ3JhZGllbnQKMi4gTkRWSSAodGhpcyBpcyBhbHJlYWR5IHZhcnlpbmcgbW9udGhseSkKICAtIEdyYWRpZW50IGFuZCBsb2NhdGlvbgozLiBNYXN0aW5nIGRlbnNpdHkKICAtIEdyYWRpZW50IGFuZCBsb2NhdGlvbgo0LiBXYXRlciB1c2FnZQogIC0gR3JhZGllbnQKICAKYGBge3J9CnNvdXJjZSgicGlnZnhucy5SIikKdGRhdCA9IGRhdFtwaWdJRCA9PSAidGVqb25NMzAyIl0KdGRhdFsgLCBkYXRldGltZTo9YXMuUE9TSVhjdCh0LCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gJ0dNVCcpXQp0ZGF0WyAsIGhvdXJvZmRheTo9c2NhbGUoaG91cihkYXRldGltZSkpXQp0ZGF0WyAsIG1vbnRob2Z5ZWFyOj1tb250aChkYXRldGltZSldCnNlYXNvbnMgPSBsaXN0KCJzdW1tZXIiID0gYyg2LCA3LCA4KSwgImZhbGwiID0gYyg5LCAxMCwgMTEpLCAKICAgICAgICAgICAgICAgIndpbnRlciI9IGMoMTIsIDEsIDIpLCAic3ByaW5nIj1jKDMsIDQsIDUpKQoKZGZfaG91ciA9IDUKc3RkY29scyA9IGMoIm5kdmlfbG9jIiwgIm5kdmlfZ3JhZCIsICJtYXN0aW5nX2xvYyIsCiAgICAgICAgICAgICJtYXN0aW5nX2dyYWQiLCAid2F0ZXJfZ3JhZCIsCiAgICAgICAgICAgICJlbGV2YXRpb25fbG9jIiwgImVsZXZhdGlvbl9ncmFkIiwKICAgICAgICAgICAgImZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCIsCiAgICAgICAgICAgICJjYW5vcHljb3Zlcl9sb2MiLCAiY2Fub3B5Y292ZXJfZ3JhZCIsICJjcnciKQpub25zdGRjb2xzID0gYygiZnJ1aXRfYW5kX251dHNfbG9jIikKc3BsaW5lY29scyA9IGMoIm5kdmlfZ3JhZCIsICJtYXN0aW5nX2dyYWQiLCAiZWxldmF0aW9uX2dyYWQiLCAiZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkIiwKICAgICAgICAgICAgICAgICJjYW5vcHljb3Zlcl9ncmFkIiwgIndhdGVyX2dyYWQiLCAiY3J3IikKc2Vhc29uYWxjb2xzID0gYygiZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkIiwgIm1hc3RpbmdfZ3JhZCIsICJtYXN0aW5nX2xvYyIsICJuZHZpX2dyYWQiLCAibmR2aV9sb2MiLCAid2F0ZXJfZ3JhZCIpCnNvdXJjZSgicGlnZnhucy5SIikKWHNlYXNvbiA9IGJ1aWxkX3NlYXNvbmFsX2Rlc2lnbl9tYXRyaXgodGRhdCwgc3RkY29scywgbm9uc3RkY29scywgc3BsaW5lY29scywgc2Vhc29uYWxjb2xzLCBkZl9ob3VyPWRmX2hvdXIpCgpyZWdpc3RlckRvTUMoY29yZXM9NCkKZml0c2Vhc29uID0gY3YuZ2xtbmV0KFhzZWFzb24sIHk9dGRhdCR6LCBvZmZzZXQ9bG9nKHRkYXQkdGF1KSwgZmFtaWx5PSJwb2lzc29uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgYWxwaGE9MSwgbmxhbWJkYT0yMCwgbmZvbGRzPTUsIHBhcmFsbGVsPVRSVUUpCgpgYGAKCgpgYGB7cn0KYmVzdGJldGFzID0gZml0c2Vhc29uJGdsbW5ldC5maXQkYmV0YVssIHdoaWNoKGZpdHNlYXNvbiRsYW1iZGEgPT0gZml0c2Vhc29uJGxhbWJkYS5taW4pLCBkcm9wPVRdCmJlc3RiZXRhcwpgYGAKClBsb3QgdGhlIHNlYXNvbmFsIGxvY2F0aW9uIGVmZmVjdHMKCmBgYHtyfQojIFdoaWNoIHNlYXNvbnMgd2FzIHRoZSBwaWcgaW4/CnJzdW1zID0gYXBwbHkoWHNlYXNvblssIG5hbWVzKHNlYXNvbnMpXSwgMiwgc3VtKQppbnNlYXNvbiA9IG5hbWVzKHJzdW1zKVtyc3VtcyAhPSAwXQoKIyBMb2NhdGlvbiBlZmZlY3RzCmxvY3BhcmFtcyA9IGJlc3RiZXRhc1tuYW1lcyhiZXN0YmV0YXMpICVsaWtlJSAiKmxvYyoiXQp1bnFwYXJhbXMgPSB1bmlxdWUoZG8uY2FsbChjLCBsYXBwbHkoc3Ryc3BsaXQobmFtZXMobG9jcGFyYW1zKSwgIjoiKSwgZnVuY3Rpb24oeCkgeFtbMV1dKSkpCgpmb3IocHJtIGluIHVucXBhcmFtcyl7CiAgcHJpbnQobG9jcGFyYW1zW25hbWVzKGxvY3BhcmFtcykgJWxpa2UlIHBybV0pCn0KCmBgYAoKYGBge3J9CnNwbGluZWhvdXIgPSBzKGhvdXJvZmRheSwgYnM9ImNjIiwgaz1kZl9ob3VyKQpwcmVkX2hvdXJzID0gUHJlZGljdC5tYXRyaXgoc21vb3RoLmNvbnN0cnVjdDIoc3BsaW5laG91ciwgdGRhdCwgTlVMTCksIGRhdGEuZnJhbWUoaG91cm9mZGF5PXNvcnQodW5pcXVlKHRkYXQkaG91cm9mZGF5KSkpKSAjIEItc3BsaW5lIGJhc2lzIGZvciBob3VyIGVmZmVjdAoKZGFpbHlyZXMgPSBsaXN0KCkKcGxpc3QgPSBjKCJiYXNlX2hvdXJfKiIsICJlbGV2YXRpb25fZ3JhZF8qIiwiY2Fub3B5Y292ZXJfZ3JhZF8qIiwgImNyd18qIikKI3BsaXN0ID0gYygiY2Fub3B5Y292ZXJfbG9jX2hvdXJfKiIpCmZvcihwYXR0ZXJuIGluIHBsaXN0KSB7CiAgCiAgZWZmID0gcHJlZF9ob3VycyAlKiUgdCh0KGJlc3RiZXRhc1tuYW1lcyhiZXN0YmV0YXMpICVsaWtlJSBwYXR0ZXJuXSkpCiAgZWZmZHQgPSBkYXRhLmZyYW1lKGhvdXI9MDoyMywgYmV0YT1lZmYsIGNvZWY9cGF0dGVybikKICAKICBkYWlseXJlc1tbcGF0dGVybl1dID0gZWZmZHQKfQoKZGFpbHlyZXMgPSBkby5jYWxsKHJiaW5kLCBkYWlseXJlcykKdHBsb3QgPSBnZ3Bsb3QoZGFpbHlyZXMpICsgZ2VvbV9saW5lKGFlcyh4PWhvdXIsIHk9YmV0YSkpICsgeGxhYigiSG91ciBvZiBkYXkiKSArIHlsYWIoIkVmZmVjdCBvZiBvbiBwaWcgbW92ZW1lbnQiKSArIHRoZW1lX2J3KCkgKyBmYWNldF93cmFwKH5jb2VmLCBzY2FsZXM9ImZyZWUiKQpwcmludCh0cGxvdCkKYGBgCgpgYGB7cn0Kc3BsaW5laG91ciA9IHMoaG91cm9mZGF5LCBicz0iY2MiLCBrPWRmX2hvdXIpCnByZWRfaG91cnMgPSBQcmVkaWN0Lm1hdHJpeChzbW9vdGguY29uc3RydWN0MihzcGxpbmVob3VyLCB0ZGF0LCBOVUxMKSwgZGF0YS5mcmFtZShob3Vyb2ZkYXk9c29ydCh1bmlxdWUodGRhdCRob3Vyb2ZkYXkpKSkpICMgQi1zcGxpbmUgYmFzaXMgZm9yIGhvdXIgZWZmZWN0CgpkYWlseXJlcyA9IGxpc3QoKQpyc3VtcyA9IGFwcGx5KFhzZWFzb25bLCBuYW1lcyhzZWFzb25zKV0sIDIsIHN1bSkKaW5zZWFzb24gPSBuYW1lcyhyc3VtcylbcnN1bXMgIT0gMF0KCnBsaXN0ID0gYygibmR2aV9ncmFkXyoiLCAibWFzdGluZ19ncmFkXyoiLCAiZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkXyoiLCAid2F0ZXJfZ3JhZF8qIikKI3BsaXN0ID0gYygiY2Fub3B5Y292ZXJfbG9jX2hvdXJfKiIpCmZvcihwYXR0ZXJuIGluIHBsaXN0KSB7CiAgCiAgZGFpbHlyZXNbW3BhdHRlcm5dXSA9IGxpc3QoKQogIHNlYXNiZXRhcyA9IGJlc3RiZXRhc1tuYW1lcyhiZXN0YmV0YXMpICVsaWtlJSBwYXR0ZXJuXQogIAogIGZvcihzZWFzb24gaW4gaW5zZWFzb24pewogICAgZWZmID0gcHJlZF9ob3VycyAlKiUgdCh0KHNlYXNiZXRhc1tuYW1lcyhzZWFzYmV0YXMpICVsaWtlJSBzZWFzb25dKSkKICAgIGVmZmR0ID0gZGF0YS5mcmFtZShob3VyPTA6MjMsIGJldGE9ZWZmLCBjb2VmPXBhdHRlcm4pCiAgICBlZmZkdCRzZWFzb24gPSBzZWFzb24KICAgIGRhaWx5cmVzW1twYXR0ZXJuXV1bW3NlYXNvbl1dID0gZWZmZHQKICB9Cn0KCnNlYXNvbnJlcyA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShkYWlseXJlcywgZnVuY3Rpb24oeCkgZG8uY2FsbChyYmluZCwgeCkpKQp0cGxvdCA9IGdncGxvdChzZWFzb25yZXMpICsgZ2VvbV9saW5lKGFlcyh4PWhvdXIsIHk9YmV0YSwgbGluZXR5cGU9c2Vhc29uKSkgKyB4bGFiKCJIb3VyIG9mIGRheSIpICsgeWxhYigiRWZmZWN0IG9mIG9uIHBpZyBtb3ZlbWVudCIpICsgdGhlbWVfYncoKSArIGZhY2V0X3dyYXAofmNvZWYsIHNjYWxlcz0iZnJlZSIpCnByaW50KHRwbG90KQpgYGAKCgojIyBGaXQgc2Vhc29uYWwgbW9kZWwgZm9yIGFsbCBwaWdzCgpgYGB7cn0KCnNvdXJjZSgicGlnZnhucy5SIikKCmFsbHNlYXNiZXRhcyA9IGxpc3QoKQpzcGxpbmVsaXN0ID0gbGlzdCgpCmZvcihwaWcgaW4gdW5pcXVlKGRhdCRwaWdJRCkpewogIAogIGNhdCgiRml0dGluZyIsIHBpZywgIlxuIikKICB0ZGF0ID0gZGF0W3BpZ0lEID09IHBpZ10KICB0ZGF0WyAsIGRhdGV0aW1lOj1hcy5QT1NJWGN0KHQsIG9yaWdpbiA9ICcxOTcwLTAxLTAxJywgdHogPSAnR01UJyldCiAgdGRhdFsgLCBob3Vyb2ZkYXk6PXNjYWxlKGhvdXIoZGF0ZXRpbWUpKV0KICB0ZGF0WyAsIG1vbnRob2Z5ZWFyOj1tb250aChkYXRldGltZSldCiAgCiAgIyBTZXQgZm5ncmFkIHRvIDAgaWYgaXQgaXMgTkEKICBpZihhbGwoaXMubmEodGRhdCRmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQpKSkKICAgICBmbmdyYWQgPSByZXAoMCwgbnJvdyh0ZGF0KSkKICBlbHNlCiAgICBmbmdyYWQgPSB0ZGF0JGZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZAogIAogIHRkYXQkZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkID0gZm5ncmFkCiAgCiAgCiAgc2Vhc29ucyA9IGxpc3QoInN1bW1lciIgPSBjKDYsIDcsIDgpLCAiZmFsbCIgPSBjKDksIDEwLCAxMSksIAogICAgICAgICAgICAgICAgICJ3aW50ZXIiPSBjKDEyLCAxLCAyKSwgInNwcmluZyI9YygzLCA0LCA1KSkKICAKICBkZl9ob3VyID0gNQogIHN0ZGNvbHMgPSBjKCJuZHZpX2xvYyIsICJuZHZpX2dyYWQiLCAibWFzdGluZ19sb2MiLAogICAgICAgICAgICAgICJtYXN0aW5nX2dyYWQiLCAid2F0ZXJfZ3JhZCIsCiAgICAgICAgICAgICAgImVsZXZhdGlvbl9sb2MiLCAiZWxldmF0aW9uX2dyYWQiLAogICAgICAgICAgICAgICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQiLAogICAgICAgICAgICAgICJjYW5vcHljb3Zlcl9sb2MiLCAiY2Fub3B5Y292ZXJfZ3JhZCIsICJjcnciKQogIG5vbnN0ZGNvbHMgPSBjKCJmcnVpdF9hbmRfbnV0c19sb2MiKQogIHNwbGluZWNvbHMgPSBjKCJuZHZpX2dyYWQiLCAibWFzdGluZ19ncmFkIiwgImVsZXZhdGlvbl9ncmFkIiwgImZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCIsCiAgICAgICAgICAgICAgICAgImNhbm9weWNvdmVyX2dyYWQiLCAid2F0ZXJfZ3JhZCIsICJjcnciKQogIHNlYXNvbmFsY29scyA9IGMoImZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCIsICJtYXN0aW5nX2dyYWQiLCAibWFzdGluZ19sb2MiLCAibmR2aV9ncmFkIiwgIm5kdmlfbG9jIiwgIndhdGVyX2dyYWQiLAogICAgICAgICAgICAgICAgICAgICJjcnciKQogIFhzZWFzb24gPSBidWlsZF9zZWFzb25hbF9kZXNpZ25fbWF0cml4KHRkYXQsIHN0ZGNvbHMsIG5vbnN0ZGNvbHMsIHNwbGluZWNvbHMsIHNlYXNvbmFsY29scywgZGZfaG91cj1kZl9ob3VyKQogIAogIHJlZ2lzdGVyRG9NQyhjb3Jlcz00KQogIGZpdHNlYXNvbiA9IGN2LmdsbW5ldChYc2Vhc29uLCB5PXRkYXQkeiwgb2Zmc2V0PWxvZyh0ZGF0JHRhdSksIGZhbWlseT0icG9pc3NvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYT0xLCBubGFtYmRhPTIwLCBuZm9sZHM9NSwgcGFyYWxsZWw9VFJVRSkKICAKICBiZXN0YmV0YXMgPSBmaXRzZWFzb24kZ2xtbmV0LmZpdCRiZXRhWywgd2hpY2goZml0c2Vhc29uJGxhbWJkYSA9PSBmaXRzZWFzb24kbGFtYmRhLjFzZSksIGRyb3A9VF0KICAKICBhbGxzZWFzYmV0YXNbW3BpZ11dID0gYmVzdGJldGFzCiAgCiAgc3BsaW5laG91ciA9IHMoaG91cm9mZGF5LCBicz0iY2MiLCBrPWRmX2hvdXIpCiAgcHJlZF9ob3VycyA9IFByZWRpY3QubWF0cml4KHNtb290aC5jb25zdHJ1Y3QyKHNwbGluZWhvdXIsIHRkYXQsIE5VTEwpLCBkYXRhLmZyYW1lKGhvdXJvZmRheT1zb3J0KHVuaXF1ZSh0ZGF0JGhvdXJvZmRheSkpKSkgIyBCLXNwbGluZSBiYXNpcyBmb3IgaG91ciBlZmZlY3QKICAKICBkYWlseXJlcyA9IGxpc3QoKQogIHJzdW1zID0gYXBwbHkoWHNlYXNvblssIG5hbWVzKHNlYXNvbnMpXSwgMiwgc3VtKQogIGluc2Vhc29uID0gbmFtZXMocnN1bXMpW3JzdW1zICE9IDBdCiAgCiAgcGxpc3QgPSBjKCJuZHZpX2dyYWRfKiIsICJtYXN0aW5nX2dyYWRfKiIsICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWRfKiIsICJ3YXRlcl9ncmFkXyoiLCAiY3J3XyoiLCAiYmFzZV9ob3VyXyoiKQogICNwbGlzdCA9IGMoImNhbm9weWNvdmVyX2xvY19ob3VyXyoiKQogIGZvcihwYXR0ZXJuIGluIHBsaXN0KSB7CiAgICAKICAgIGRhaWx5cmVzW1twYXR0ZXJuXV0gPSBsaXN0KCkKICAgIHNlYXNiZXRhcyA9IGJlc3RiZXRhc1tuYW1lcyhiZXN0YmV0YXMpICVsaWtlJSBwYXR0ZXJuXQogICAgCiAgICBmb3Ioc2Vhc29uIGluIGluc2Vhc29uKXsKICAgICAgZWZmID0gcHJlZF9ob3VycyAlKiUgdCh0KHNlYXNiZXRhc1tuYW1lcyhzZWFzYmV0YXMpICVsaWtlJSBzZWFzb25dKSkKICAgICAgZWZmZHQgPSBkYXRhLmZyYW1lKGhvdXI9MDoyMywgYmV0YT1lZmYsIGNvZWY9cGF0dGVybikKICAgICAgZWZmZHQkc2Vhc29uID0gc2Vhc29uCiAgICAgIGRhaWx5cmVzW1twYXR0ZXJuXV1bW3NlYXNvbl1dID0gZWZmZHQKICAgIH0KICB9CiAgCiAgc2Vhc29ucmVzID0gZG8uY2FsbChyYmluZCwgbGFwcGx5KGRhaWx5cmVzLCBmdW5jdGlvbih4KSBkby5jYWxsKHJiaW5kLCB4KSkpCiAgc2Vhc29ucmVzJHBpZ0lEID0gcGlnCiAgc3BsaW5lbGlzdFtbcGlnXV0gPSBzZWFzb25yZXMKICAKfQoKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQoKYWxscGlnc19zZWFzb25jb2VmID0gYXMuZGF0YS50YWJsZShkby5jYWxsKHJiaW5kLCBzcGxpbmVsaXN0KSkKYWxscGlnc19zZWFzb25jb2VmJHNlYXNvbiA9IGZhY3RvcihhbGxwaWdzX3NlYXNvbmNvZWYkc2Vhc29uLCBsZXZlbHM9Yygid2ludGVyIiwgInNwcmluZyIsICJzdW1tZXIiLCAiZmFsbCIpKQptZWFucGlncyA9IGFsbHBpZ3Nfc2Vhc29uY29lZlssIGxpc3QobWVhbmJldGE9bWVhbihiZXRhKSksIGJ5PWxpc3QoY29lZiwgc2Vhc29uLCBob3VyKV0KbWVhbnBpZ3Mkc2Vhc29uID0gZmFjdG9yKG1lYW5waWdzJHNlYXNvbiwgbGV2ZWxzPWMoIndpbnRlciIsICJzcHJpbmciLCAic3VtbWVyIiwgImZhbGwiKSkKCnRwbG90ID0gZ2dwbG90KGRhdGE9TlVMTCkgKyBnZW9tX2xpbmUoZGF0YT1hbGxwaWdzX3NlYXNvbmNvZWYsIGFlcyh4PWhvdXIsIHk9YmV0YSwgY29sb3I9cGlnSUQpLCBhbHBoYT0wLjUpICsgZ2VvbV9saW5lKGRhdGE9bWVhbnBpZ3MsIGFlcyh4PWhvdXIsIHk9bWVhbmJldGEpLCBzaXplPTEpICsgCiAgICAgICAgICAgICAgICAgICAgeGxhYigiSG91ciBvZiBkYXkiKSArIHlsYWIoIkVmZmVjdCBvZiBvbiBwaWcgbW92ZW1lbnQiKSArIHRoZW1lX2J3KCkgKyBmYWNldF93cmFwKH5jb2VmICsgc2Vhc29uLCBzY2FsZXM9ImZyZWUiLCBucm93PTYsIG5jb2w9NCkKcHJpbnQodHBsb3QpCmBgYAoKUGxvdCB0aGUgdmFyaWFibGUgbG9jYXRpb24gY29lZmZpY2llbnRzIGJ5IHNlYXNvbgoKYGBge3J9CmNvbWJldGFzID0gZG8uY2FsbChyYmluZCwgYWxsc2Vhc2JldGFzKQoKIyBFeHRyYWN0IHRoZSBsb2NhdGlvbiBjb2x1bW5zCmNvbWJldGFzID0gY29tYmV0YXNbICwgY29sbmFtZXMoY29tYmV0YXMpICVsaWtlJSAiKl9sb2MqIl0KbWNvbWJldGFzID0gbWVsdChjb21iZXRhcykKY29sbmFtZXMobWNvbWJldGFzKSA9IGMoInBpZyIsICJjb2VmIiwgInZhbHVlIikKbWNvbWJldGFzJHNlYXNvbiA9IHNhcHBseShzdHJzcGxpdChhcy5jaGFyYWN0ZXIobWNvbWJldGFzJGNvZWYpLCAiOiIpLCBmdW5jdGlvbih4KSB4WzJdKQptY29tYmV0YXMkY29lZjEgPSBzYXBwbHkoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKG1jb21iZXRhcyRjb2VmKSwgIjoiKSwgZnVuY3Rpb24oeCkgeFsxXSkKCm1jb21iZXRhcyRzZWFzb25baXMubmEobWNvbWJldGFzJHNlYXNvbildID0gIm5vbnNlYXNvbmFsIgptY29tYmV0YXMkc2Vhc29uID0gZmFjdG9yKG1jb21iZXRhcyRzZWFzb24sIGxldmVscz1jKCJ3aW50ZXIiLCAic3ByaW5nIiwgInN1bW1lciIsICJmYWxsIiwgIm5vbnNlYXNvbmFsIikpCmdncGxvdChtY29tYmV0YXMpICsgZ2VvbV9ib3hwbG90KGFlcyh4PXNlYXNvbiwgeT12YWx1ZSkpICsgZmFjZXRfd3JhcCh+Y29lZjEsIHNjYWxlcz0iZnJlZSIpICsgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCgpgYGAKClNpbWlsYXIgdG8gdGhlIHByZXZpb3VzIGFuYWx5c2lzLiAgV2Ugc2VlIGFuIGluY3JlYXNlZCB0ZW5kZW5jeSB0byBsaW5nZXIgaW4gYXJlYXMgd2l0aCBoaWdoIG1hc3RpbmcgdHJlZSBkZW5zaXR5IGluIHRoZSB3aW50ZXIgKHRob3VnaCB0aGlzIG1pZ2h0IGJlIGRyaXZlbiBieSB2ZXJ5IGZldyBwb2ludHMpLiBUaGUgcmVtYWluZGVyIG9mIHRoZSB5ZWFyIHRoZXJlIGlzIG5vdCBhcyBtdWNoIHNlbGVjdGlvbiBmb3IgYXJlYXMgd2l0aCBoaWdoIG1hc3RpbmcgZGVuc2l0eS4KCgojIyBUZW1wZXJhdHVyZSBhbmQgcHJlY2lwaXRhdGlvbiBhbmFseXNpcwoKSW5zdGVhZCBvZiB1c2luZyBhIHNlYXNvbmFsIGZhY3RvciBhcyB3ZSBkbyBpbiB0aGUgcHJldmlvdXMgYW5hbHlzaXMsIGxldCdzIHJlcGxhY2UgdGhpcyBzZWFzb25hbCBmYWN0b3Igd2l0aCBhbiBpbnRlcmFjdGlvbiBiZXR3ZWVuIG1vbnRobHkgdGVtcGVyYXR1cmUgYW5kIHByZWNpcGl0YXRpb24gYW5kIG91ciBzZWFzb25hbCB2YXJpYWJsZXMuCgpgYGB7cn0KdGRhdCA9IGRhdFtwaWdJRCA9PSAidGVqb25NMzAyIl0KdGRhdFsgLCBkYXRldGltZTo9YXMuUE9TSVhjdCh0LCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gJ0dNVCcpXQp0ZGF0WyAsIGhvdXJvZmRheTo9c2NhbGUoaG91cihkYXRldGltZSkpXQp0ZGF0WyAsIG1vbnRob2Z5ZWFyOj1tb250aChkYXRldGltZSldCgpkZl9ob3VyID0gNQpzdGRjb2xzID0gYygibmR2aV9sb2MiLCAibmR2aV9ncmFkIiwgIm1hc3RpbmdfbG9jIiwKICAgICAgICAgICAgIm1hc3RpbmdfZ3JhZCIsICJ3YXRlcl9ncmFkIiwKICAgICAgICAgICAgImVsZXZhdGlvbl9sb2MiLCAiZWxldmF0aW9uX2dyYWQiLAogICAgICAgICAgICAiZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkIiwKICAgICAgICAgICAgImNhbm9weWNvdmVyX2xvYyIsICJjYW5vcHljb3Zlcl9ncmFkIiwgImNydyIpCm5vbnN0ZGNvbHMgPSBjKCJmcnVpdF9hbmRfbnV0c19sb2MiKQpzcGxpbmVjb2xzID0gYygibmR2aV9ncmFkIiwgIm1hc3RpbmdfZ3JhZCIsICJlbGV2YXRpb25fZ3JhZCIsICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQiLAogICAgICAgICAgICAgICAiY2Fub3B5Y292ZXJfZ3JhZCIsICJ3YXRlcl9ncmFkIiwgImNydyIpCnNlYXNvbmFsY29scyA9IGMoImZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZCIsICJtYXN0aW5nX2dyYWQiLCAibWFzdGluZ19sb2MiLCAibmR2aV9ncmFkIiwgIm5kdmlfbG9jIiwgIndhdGVyX2dyYWQiLAogICAgICAgICAgICAgICAgICJjcnciKQpYc2Vhc29uID0gYnVpbGRfdGVtcHByZWNpcF9kZXNpZ25fbWF0cml4KHRkYXQsIHN0ZGNvbHMsIG5vbnN0ZGNvbHMsIHNwbGluZWNvbHMsIHNlYXNvbmFsY29scywgZGZfaG91cj1kZl9ob3VyKQoKcmVnaXN0ZXJEb01DKGNvcmVzPTQpCmZpdHNlYXNvbiA9IGN2LmdsbW5ldChYc2Vhc29uLCB5PXRkYXQkeiwgb2Zmc2V0PWxvZyh0ZGF0JHRhdSksIGZhbWlseT0icG9pc3NvbiIsIAogICAgICAgICAgICAgICAgICAgICAgYWxwaGE9MSwgbmxhbWJkYT0yMCwgbmZvbGRzPTUsIHBhcmFsbGVsPVRSVUUpCgoKYGBgCgpgYGB7cn0KYmVzdGJldGFzID0gZml0c2Vhc29uJGdsbW5ldC5maXQkYmV0YVssIHdoaWNoKGZpdHNlYXNvbiRsYW1iZGEgPT0gZml0c2Vhc29uJGxhbWJkYS4xc2UpLCBkcm9wPVRdCmJlc3RiZXRhcwpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KCiMgQnVpbGQgYSBtb250aGx5IHRlbXBlcmF0dXJlIGFuZCBwcmVjaXBpdGF0aW9uIHByZWRpY3Rvcgptb250aHRlbXAgPSBkYXRbLCBsaXN0KHN0ZW1wPXNjYWxlMih0ZW1wZXJhdHVyZV9sb2MpLCBtb250aG9meWVhcildWywgbGlzdCh0ZW1wZXJhdHVyZV9sb2M9bWVhbihzdGVtcCkpLCBieT1tb250aG9meWVhcl0KbW9udGhwcmVjaXAgPSBkYXRbLCBsaXN0KHNwcmVjaXA9c2NhbGUyKHByZWNpcGl0YXRpb25fbG9jKSwgbW9udGhvZnllYXIpXVssIGxpc3QocHJlY2lwaXRhdGlvbl9sb2M9bWVhbihzcHJlY2lwKSksIGJ5PW1vbnRob2Z5ZWFyXQptb250aHRwID0gY2JpbmQobW9udGh0ZW1wLCBtb250aHByZWNpcFssIGxpc3QocHJlY2lwaXRhdGlvbl9sb2MpXSkKbW9udGh0cFssICgidGVtcGVyYXR1cmVfbG9jOnByZWNpcGl0YXRpb25fbG9jIik6PXRlbXBlcmF0dXJlX2xvYypwcmVjaXBpdGF0aW9uX2xvY10KCnNwbGluZWhvdXIgPSBzKGhvdXJvZmRheSwgYnM9ImNjIiwgaz1kZl9ob3VyKQpwcmVkX2hvdXJzID0gUHJlZGljdC5tYXRyaXgoc21vb3RoLmNvbnN0cnVjdDIoc3BsaW5laG91ciwgdGRhdCwgTlVMTCksIGRhdGEuZnJhbWUoaG91cm9mZGF5PXNvcnQodW5pcXVlKHRkYXQkaG91cm9mZGF5KSkpKSAjIEItc3BsaW5lIGJhc2lzIGZvciBob3VyIGVmZmVjdAoKZGFpbHlyZXMgPSBsaXN0KCkKaW5tb250aCA9IHVuaXF1ZSh0ZGF0JG1vbnRob2Z5ZWFyKQoKcGxpc3QgPSBjKCJuZHZpX2dyYWRfKiIsICJtYXN0aW5nX2dyYWRfKiIsICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWRfKiIsICJ3YXRlcl9ncmFkXyoiLCAiY3J3XyoiLCAiYmFzZV9ob3VyXyoiKQp0cGNvbHMgPSBjKCJ0ZW1wZXJhdHVyZV9sb2MiLCAicHJlY2lwaXRhdGlvbl9sb2MiLCAidGVtcGVyYXR1cmVfbG9jOnByZWNpcGl0YXRpb25fbG9jIikKI3BsaXN0ID0gYygiY2Fub3B5Y292ZXJfbG9jX2hvdXJfKiIpCgpmb3IocGF0dGVybiBpbiBwbGlzdCkgeyAjIExvb3AgdGhyb3VnaCBlYWNoIHBhdHRlcm4KICAKICBkYWlseXJlc1tbcGF0dGVybl1dID0gbGlzdCgpCiAgdHBiZXRhcyA9IGJlc3RiZXRhc1tuYW1lcyhiZXN0YmV0YXMpICVsaWtlJSBwYXR0ZXJuXQogIAogIGZvcihtb250aCBpbiBpbm1vbnRoKXsKICAgICAgCiAgICAgIGJhc2VuYW1lcyA9IHBhc3RlMChzdHJzcGxpdChwYXR0ZXJuLCAiKiIsIGZpeGVkPVQpW1sxXV0sIDE6KGRmX2hvdXIgLSAxKSkKICAgICAgYmFzZWVmZiA9IHByZWRfaG91cnMgJSolIHQodCh0cGJldGFzW2Jhc2VuYW1lc10pKQogICAgICAKICAgICAgZm9yKHRwIGluIHRwY29scyl7CiAgICAgICAgCiAgICAgICAgdG5hbWVzID0gcGFzdGUwKHN0cnNwbGl0KHBhdHRlcm4sICIqIiwgZml4ZWQ9VClbWzFdXSwgMTooZGZfaG91ciAtIDEpLCAiOiIsIHRwKQogICAgICAgIHRwdmFsID0gYXMubnVtZXJpYyhtb250aHRwW21vbnRob2Z5ZWFyID09IG1vbnRoLCB0cCwgd2l0aD1GXSkKICAgICAgICB0ZWZmID0gKHByZWRfaG91cnMqdHB2YWwpICUqJSB0KHQodHBiZXRhc1t0bmFtZXNdKSkKICAgICAgICAKICAgICAgICBiYXNlZWZmID0gYmFzZWVmZiArIHRlZmYKICAgICAgfQogICAgICAgIAogICAgICBlZmZkdCA9IGRhdGEuZnJhbWUoaG91cj0wOjIzLCBiZXRhPWJhc2VlZmYsIGNvZWY9cGF0dGVybikKICAgICAgZWZmZHQkbW9udGhvZnllYXIgPSBtb250aAogICAgICBkYWlseXJlc1tbcGF0dGVybl1dW1ttb250aF1dID0gZWZmZHQKICB9Cn0KCm1vbnRocmVzID0gZG8uY2FsbChyYmluZCwgbGFwcGx5KGRhaWx5cmVzLCBmdW5jdGlvbih4KSBkby5jYWxsKHJiaW5kLCB4KSkpCnRwbG90ID0gZ2dwbG90KG1vbnRocmVzKSArIGdlb21fbGluZShhZXMoeD1ob3VyLCB5PWJldGEsIGNvbG9yPWFzLmZhY3Rvcihtb250aG9meWVhcikpKSArIHhsYWIoIkhvdXIgb2YgZGF5IikgKyB5bGFiKCJFZmZlY3Qgb24gcGlnIG1vdmVtZW50IikgKyB0aGVtZV9idygpICsgZmFjZXRfd3JhcCh+Y29lZiwgc2NhbGVzPSJmcmVlIikKCnRwcGxvdCA9IGdncGxvdChtb250aHRwKSArIGdlb21fbGluZShhZXMoeD1tb250aG9meWVhciwgeT10ZW1wZXJhdHVyZV9sb2MsIGNvbG9yPSJ0ZW1wZXJhdHVyZSIpKSArIAogICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX2xpbmUoYWVzKHg9bW9udGhvZnllYXIsIHk9cHJlY2lwaXRhdGlvbl9sb2MsIGNvbG9yPSJwcmVjaXBpdGF0aW9uIikpICsgdGhlbWVfYncoKSArIHhsYWIoIk1vbnRoIG9mIHllYXIiKSArIHlsYWIoIlN0YW5kYXJkaXplZCBtZWFuIG1vbnRoeSB2YWx1ZSIpCgpyZXF1aXJlKGdyaWRFeHRyYSkKZ3JpZC5hcnJhbmdlKHRwbG90LCB0cHBsb3QsIG5yb3c9MSwgbmNvbD0yKQoKYGBgCgoKUGxvdCBob3cgdGhlIG5vbi1kYWlseSB2YXJ5aW5nIGxvY2F0aW9uLWJhc2VkIHZhcmlhYmxlcyBjaGFuZ2Ugd2l0aCB0ZW1wZXJhdHVyZQoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTJ9CmJhc2VuYW1lcyA9IHNhcHBseShzdHJzcGxpdChuYW1lcyhiZXN0YmV0YXMpLCAiOiIsIGZpeGVkPVQpLCBmdW5jdGlvbih4KSB4W1sxXV1bMV0pCmxvY3BhcmFtcyA9IGJlc3RiZXRhc1tiYXNlbmFtZXMgJWxpa2UlICIqX2xvYyoiXQoKc2Vhc19scHMgPSBjKCJuZHZpX2xvYyIsICJtYXN0aW5nX2xvYyIpIAoKCmxvY2VmZmVjdHMgPSBsaXN0KCkKZm9yKHBhcmFtIGluIHNlYXNfbHBzKXsKICAKICBsb2NlZmZlY3RzW1twYXJhbV1dID0gbGlzdCgpCiAgCiAgZm9yKG1vbnRoIGluIGlubW9udGgpewogICAgCiAgICBiYXNlZWZmID0gbG9jcGFyYW1zW3BhcmFtXQogICAgCiAgICBmb3IodHAgaW4gdHBjb2xzKXsKICAgICAgCiAgICAgIHRwdmFsID0gYXMubnVtZXJpYyhtb250aHRwW21vbnRob2Z5ZWFyID09IG1vbnRoLCB0cCwgd2l0aD1GXSkKICAgICAgYmFzZWVmZiA9IGJhc2VlZmYgKyB0cHZhbCpsb2NwYXJhbXNbcGFzdGUwKHBhcmFtLCAiOiIsIHRwKV0KICAgICAgCiAgICB9CiAgICAKICAgIGxvY2VmZmVjdHNbW3BhcmFtXV1bW21vbnRoXV0gPSBkYXRhLmZyYW1lKG1vbnRoPW1vbnRoLCBjb2VmPXBhcmFtLCBiZXRhPWJhc2VlZmYpCiAgfQogIAp9Cgptb250aGxvYyA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShsb2NlZmZlY3RzLCBmdW5jdGlvbih4KSBkby5jYWxsKHJiaW5kLCB4KSkpCnRwbG90ID0gZ2dwbG90KG1vbnRobG9jKSArIGdlb21fcGF0aChhZXMoeD1tb250aCwgeT1iZXRhKSkgKyB4bGFiKCJNb250aCBvZiB5ZWFyIikgKyB5bGFiKCJFZmZlY3Qgb24gcGlnIG1vdmVtZW50IikgKyB0aGVtZV9idygpICsgZmFjZXRfd3JhcCh+Y29lZikKI3RwbG90Cgp0cHBsb3QgPSBnZ3Bsb3QobW9udGh0cCkgKyBnZW9tX2xpbmUoYWVzKHg9bW9udGhvZnllYXIsIHk9dGVtcGVyYXR1cmVfbG9jLCBjb2xvcj0idGVtcGVyYXR1cmUiKSkgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9saW5lKGFlcyh4PW1vbnRob2Z5ZWFyLCB5PXByZWNpcGl0YXRpb25fbG9jLCBjb2xvcj0icHJlY2lwaXRhdGlvbiIpKSArIHRoZW1lX2J3KCkgKyB4bGFiKCJNb250aCBvZiB5ZWFyIikgKyB5bGFiKCJTdGFuZGFyZGl6ZWQgbWVhbiBtb250aHkgdmFsdWUiKQoKcmVxdWlyZShncmlkRXh0cmEpCmdyaWQuYXJyYW5nZSh0cGxvdCwgdHBwbG90LCBucm93PTEsIG5jb2w9MikKCmBgYAoKIyMgQW5hbHlzaXMgb24gYWxsIHBpZ3MKCmBgYHtyfQoKYWxsdHByZXMgPSBsaXN0KCkKYWxsdHBiZXN0YmV0YXMgPSBsaXN0KCkKCmZvcihwaWcgaW4gdW5pcXVlKGRhdCRwaWdJRCkpewogIAogIGNhdCgiV29ya2luZyBvbiIsIHBpZywgIlxuIikKICB0ZGF0ID0gZGF0W3BpZ0lEID09IHBpZ10KICB0ZGF0WyAsIGRhdGV0aW1lOj1hcy5QT1NJWGN0KHQsIG9yaWdpbiA9ICcxOTcwLTAxLTAxJywgdHogPSAnR01UJyldCiAgdGRhdFsgLCBob3Vyb2ZkYXk6PXNjYWxlKGhvdXIoZGF0ZXRpbWUpKV0KICB0ZGF0WyAsIG1vbnRob2Z5ZWFyOj1tb250aChkYXRldGltZSldCiAgCiAgIyBTZXQgZm5ncmFkIHRvIDAgaWYgaXQgaXMgTkEKICBpZihhbGwoaXMubmEodGRhdCRmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQpKSkKICAgICBmbmdyYWQgPSByZXAoMCwgbnJvdyh0ZGF0KSkKICBlbHNlCiAgICBmbmdyYWQgPSB0ZGF0JGZydWl0X2FuZF9udXRzZGlzdHNfZ3JhZAogIAogIHRkYXQkZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkID0gZm5ncmFkCiAgCiAgZGZfaG91ciA9IDUKICBzdGRjb2xzID0gYygibmR2aV9sb2MiLCAibmR2aV9ncmFkIiwgIm1hc3RpbmdfbG9jIiwKICAgICAgICAgICAgICAibWFzdGluZ19ncmFkIiwgIndhdGVyX2dyYWQiLAogICAgICAgICAgICAgICJlbGV2YXRpb25fbG9jIiwgImVsZXZhdGlvbl9ncmFkIiwKICAgICAgICAgICAgICAiZnJ1aXRfYW5kX251dHNkaXN0c19ncmFkIiwKICAgICAgICAgICAgICAiY2Fub3B5Y292ZXJfbG9jIiwgImNhbm9weWNvdmVyX2dyYWQiLCAiY3J3IikKICBub25zdGRjb2xzID0gYygiZnJ1aXRfYW5kX251dHNfbG9jIikKICBzcGxpbmVjb2xzID0gYygibmR2aV9ncmFkIiwgIm1hc3RpbmdfZ3JhZCIsICJlbGV2YXRpb25fZ3JhZCIsICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQiLAogICAgICAgICAgICAgICAgICJjYW5vcHljb3Zlcl9ncmFkIiwgIndhdGVyX2dyYWQiLCAiY3J3IikKICBzZWFzb25hbGNvbHMgPSBjKCJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWQiLCAibWFzdGluZ19ncmFkIiwgIm1hc3RpbmdfbG9jIiwgIm5kdmlfZ3JhZCIsICJuZHZpX2xvYyIsICJ3YXRlcl9ncmFkIiwKICAgICAgICAgICAgICAgICAgICJjcnciKQogIFhzZWFzb24gPSBidWlsZF90ZW1wcHJlY2lwX2Rlc2lnbl9tYXRyaXgodGRhdCwgc3RkY29scywgbm9uc3RkY29scywgc3BsaW5lY29scywgc2Vhc29uYWxjb2xzLCBkZl9ob3VyPWRmX2hvdXIpCiAgCiAgcmVnaXN0ZXJEb01DKGNvcmVzPTQpCiAgZml0c2Vhc29uID0gY3YuZ2xtbmV0KFhzZWFzb24sIHk9dGRhdCR6LCBvZmZzZXQ9bG9nKHRkYXQkdGF1KSwgZmFtaWx5PSJwb2lzc29uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhPTEsIG5sYW1iZGE9MjAsIG5mb2xkcz01LCBwYXJhbGxlbD1UUlVFKQogIAogIGJlc3RiZXRhcyA9IGZpdHNlYXNvbiRnbG1uZXQuZml0JGJldGFbLCB3aGljaChmaXRzZWFzb24kbGFtYmRhID09IGZpdHNlYXNvbiRsYW1iZGEuMXNlKSwgZHJvcD1UXQogIAogICMgQnVpbGQgYSBtb250aGx5IHRlbXBlcmF0dXJlIGFuZCBwcmVjaXBpdGF0aW9uIHByZWRpY3RvcgogIG1vbnRodGVtcCA9IGRhdFssIGxpc3Qoc3RlbXA9c2NhbGUyKHRlbXBlcmF0dXJlX2xvYyksIG1vbnRob2Z5ZWFyKV1bLCBsaXN0KHRlbXBlcmF0dXJlX2xvYz1tZWFuKHN0ZW1wKSksIGJ5PW1vbnRob2Z5ZWFyXQogIG1vbnRocHJlY2lwID0gZGF0WywgbGlzdChzcHJlY2lwPXNjYWxlMihwcmVjaXBpdGF0aW9uX2xvYyksIG1vbnRob2Z5ZWFyKV1bLCBsaXN0KHByZWNpcGl0YXRpb25fbG9jPW1lYW4oc3ByZWNpcCkpLCBieT1tb250aG9meWVhcl0KICBtb250aHRwID0gY2JpbmQobW9udGh0ZW1wLCBtb250aHByZWNpcFssIGxpc3QocHJlY2lwaXRhdGlvbl9sb2MpXSkKICBtb250aHRwWywgKCJ0ZW1wZXJhdHVyZV9sb2M6cHJlY2lwaXRhdGlvbl9sb2MiKTo9dGVtcGVyYXR1cmVfbG9jKnByZWNpcGl0YXRpb25fbG9jXQogIAogIHNwbGluZWhvdXIgPSBzKGhvdXJvZmRheSwgYnM9ImNjIiwgaz1kZl9ob3VyKQogIHByZWRfaG91cnMgPSBQcmVkaWN0Lm1hdHJpeChzbW9vdGguY29uc3RydWN0MihzcGxpbmVob3VyLCB0ZGF0LCBOVUxMKSwgZGF0YS5mcmFtZShob3Vyb2ZkYXk9c29ydCh1bmlxdWUodGRhdCRob3Vyb2ZkYXkpKSkpICMgQi1zcGxpbmUgYmFzaXMgZm9yIGhvdXIgZWZmZWN0CiAgCiAgZGFpbHlyZXMgPSBsaXN0KCkKICBpbm1vbnRoID0gdW5pcXVlKHRkYXQkbW9udGhvZnllYXIpCiAgCiAgcGxpc3QgPSBjKCJuZHZpX2dyYWRfKiIsICJtYXN0aW5nX2dyYWRfKiIsICJmcnVpdF9hbmRfbnV0c2Rpc3RzX2dyYWRfKiIsICJ3YXRlcl9ncmFkXyoiLCAiY3J3XyoiLCAiYmFzZV9ob3VyXyoiKQogIHRwY29scyA9IGMoInRlbXBlcmF0dXJlX2xvYyIsICJwcmVjaXBpdGF0aW9uX2xvYyIsICJ0ZW1wZXJhdHVyZV9sb2M6cHJlY2lwaXRhdGlvbl9sb2MiKQogICNwbGlzdCA9IGMoImNhbm9weWNvdmVyX2xvY19ob3VyXyoiKQogIAogIGZvcihwYXR0ZXJuIGluIHBsaXN0KSB7ICMgTG9vcCB0aHJvdWdoIGVhY2ggcGF0dGVybgogICAgCiAgICBkYWlseXJlc1tbcGF0dGVybl1dID0gbGlzdCgpCiAgICB0cGJldGFzID0gYmVzdGJldGFzW25hbWVzKGJlc3RiZXRhcykgJWxpa2UlIHBhdHRlcm5dCiAgICAKICAgIGZvcihtb250aCBpbiBpbm1vbnRoKXsKICAgICAgCiAgICAgIGJhc2VuYW1lcyA9IHBhc3RlMChzdHJzcGxpdChwYXR0ZXJuLCAiKiIsIGZpeGVkPVQpW1sxXV0sIDE6KGRmX2hvdXIgLSAxKSkKICAgICAgYmFzZWVmZiA9IHByZWRfaG91cnMgJSolIHQodCh0cGJldGFzW2Jhc2VuYW1lc10pKQogICAgICAKICAgICAgZm9yKHRwIGluIHRwY29scyl7CiAgICAgICAgCiAgICAgICAgdG5hbWVzID0gcGFzdGUwKHN0cnNwbGl0KHBhdHRlcm4sICIqIiwgZml4ZWQ9VClbWzFdXSwgMTooZGZfaG91ciAtIDEpLCAiOiIsIHRwKQogICAgICAgIHRwdmFsID0gYXMubnVtZXJpYyhtb250aHRwW21vbnRob2Z5ZWFyID09IG1vbnRoLCB0cCwgd2l0aD1GXSkKICAgICAgICB0ZWZmID0gKHByZWRfaG91cnMqdHB2YWwpICUqJSB0KHQodHBiZXRhc1t0bmFtZXNdKSkKICAgICAgICAKICAgICAgICBiYXNlZWZmID0gYmFzZWVmZiArIHRlZmYKICAgICAgfQogICAgICAKICAgICAgZWZmZHQgPSBkYXRhLmZyYW1lKGhvdXI9MDoyMywgYmV0YT1iYXNlZWZmLCBjb2VmPXBhdHRlcm4pCiAgICAgIGVmZmR0JG1vbnRob2Z5ZWFyID0gbW9udGgKICAgICAgZGFpbHlyZXNbW3BhdHRlcm5dXVtbbW9udGhdXSA9IGVmZmR0CiAgICB9CiAgfQogIAogIG1vbnRocmVzID0gZG8uY2FsbChyYmluZCwgbGFwcGx5KGRhaWx5cmVzLCBmdW5jdGlvbih4KSBkby5jYWxsKHJiaW5kLCB4KSkpCiAgbW9udGhyZXMkcGlnSUQgPSBwaWcKICBhbGx0cHJlc1tbcGlnXV0gPSBtb250aHJlcwogIGFsbHRwYmVzdGJldGFzW1twaWddXSA9IGJlc3RiZXRhcwogIAp9CmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEwfQphbGxwaWdzdHAgPSBhcy5kYXRhLnRhYmxlKGRvLmNhbGwocmJpbmQsIGFsbHRwcmVzKSkKbWVhbnBpZyA9IGFsbHBpZ3N0cFssIGxpc3QoYmV0YT1tZWFuKGJldGEpKSwgYnk9bGlzdChtb250aG9meWVhciwgaG91ciwgY29lZildCm1lYW5waWckcGlnSUQgPSAibWVhbnRlam9ucGlnIgptZWFucGlnID0gbWVhbnBpZ1ssIGNvbG5hbWVzKGFsbHBpZ3N0cCksIHdpdGg9Rl0KYWxscGlnc3RwZnVsbCA9IHJiaW5kKGFsbHBpZ3N0cCwgbWVhbnBpZykKCgpjYlBhbGV0dGUgPC0gYygiIzk5OTk5OSIsICIjRTY5RjAwIiwgIiM1NkI0RTkiLCAiIzAwOUU3MyIsICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikKdHBsb3QgPSBnZ3Bsb3QoYWxscGlnc3RwZnVsbCkgKyBnZW9tX2xpbmUoYWVzKHg9aG91ciwgeT1iZXRhLCBjb2xvcj1hcy5mYWN0b3IobW9udGhvZnllYXIpKSkgKyB4bGFiKCJIb3VyIG9mIGRheSIpICsgeWxhYigiRWZmZWN0IG9uIHBpZyBtb3ZlbWVudCIpICsgdGhlbWVfYncoKSArIGZhY2V0X3dyYXAofnBpZ0lEICsgY29lZiwgc2NhbGVzPSJmcmVlIiwgbmNvbD02LCBucm93PTE0KSAjKyBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKQp0cGxvdAoKYGBgCgoKCgpQbG90IHRoZSBub24tZGFpbHkgY29lZmZpY2llbnRzCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NH0KCmFsbHBpZ2xvY2VmZmVjdHMgPSBsaXN0KCkKZm9yKHBpZyBpbiBuYW1lcyhhbGx0cGJlc3RiZXRhcykpewogIAogIGlubW9udGggPSB1bmlxdWUoZGF0W3BpZ0lEID09IHBpZ10kbW9udGhvZnllYXIpCiAgYmVzdGJldGFzID0gYWxsdHBiZXN0YmV0YXNbW3BpZ11dCiAgYmFzZW5hbWVzID0gc2FwcGx5KHN0cnNwbGl0KG5hbWVzKGJlc3RiZXRhcyksICI6IiwgZml4ZWQ9VCksIGZ1bmN0aW9uKHgpIHhbWzFdXVsxXSkKICBsb2NwYXJhbXMgPSBiZXN0YmV0YXNbYmFzZW5hbWVzICVsaWtlJSAiKl9sb2MqIl0KICAKICBzZWFzX2xwcyA9IGMoIm5kdmlfbG9jIiwgIm1hc3RpbmdfbG9jIikgCiAgCiAgbG9jZWZmZWN0cyA9IGxpc3QoKQogIGZvcihwYXJhbSBpbiBzZWFzX2xwcyl7CiAgICAKICAgIGxvY2VmZmVjdHNbW3BhcmFtXV0gPSBsaXN0KCkKICAgIAogICAgZm9yKG1vbnRoIGluIGlubW9udGgpewogICAgICAKICAgICAgYmFzZWVmZiA9IGxvY3BhcmFtc1twYXJhbV0KICAgICAgCiAgICAgIGZvcih0cCBpbiB0cGNvbHMpewogICAgICAgIAogICAgICAgIHRwdmFsID0gYXMubnVtZXJpYyhtb250aHRwW21vbnRob2Z5ZWFyID09IG1vbnRoLCB0cCwgd2l0aD1GXSkKICAgICAgICBiYXNlZWZmID0gYmFzZWVmZiArIHRwdmFsKmxvY3BhcmFtc1twYXN0ZTAocGFyYW0sICI6IiwgdHApXQogICAgICAgIAogICAgICB9CiAgICAgIAogICAgICBsb2NlZmZlY3RzW1twYXJhbV1dW1ttb250aF1dID0gZGF0YS5mcmFtZShtb250aD1tb250aCwgY29lZj1wYXJhbSwgYmV0YT1iYXNlZWZmKQogICAgfQogICAgCiAgfQogIAogIG1vbnRobG9jID0gZG8uY2FsbChyYmluZCwgbGFwcGx5KGxvY2VmZmVjdHMsIGZ1bmN0aW9uKHgpIGRvLmNhbGwocmJpbmQsIHgpKSkKICBtb250aGxvYyRwaWdJRCA9IHBpZwogIGFsbHBpZ2xvY2VmZmVjdHNbW3BpZ11dID0gbW9udGhsb2MKfQoKbG9jY29lZnMgPSBhcy5kYXRhLnRhYmxlKGRvLmNhbGwocmJpbmQsIGFsbHBpZ2xvY2VmZmVjdHMpKQptbG9jY29lZnMgPSBsb2Njb2Vmc1ssIGxpc3QoYmV0YT1tZWRpYW4oYmV0YSkpLCBieT1saXN0KGNvZWYsIG1vbnRoKSBdCnRwbG90ID0gZ2dwbG90KGxvY2NvZWZzKSArIGdlb21fcG9pbnQoYWVzKHg9bW9udGgsIHk9YmV0YSkpICsgZ2VvbV9zbW9vdGgoYWVzKHg9bW9udGgsIHk9YmV0YSksIHN0YXQ9InNtb290aCIpICsgeGxhYigiTW9udGggb2YgeWVhciIpICsgeWxhYigiRWZmZWN0IG9uIHBpZyBtb3ZlbWVudCIpICsgdGhlbWVfYncoKSArIGZhY2V0X3dyYXAofmNvZWYpCnRwbG90Cgp0cHBsb3QgPSBnZ3Bsb3QobW9udGh0cCkgKyBnZW9tX2xpbmUoYWVzKHg9bW9udGhvZnllYXIsIHk9dGVtcGVyYXR1cmVfbG9jLCBjb2xvcj0idGVtcGVyYXR1cmUiKSkgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9saW5lKGFlcyh4PW1vbnRob2Z5ZWFyLCB5PXByZWNpcGl0YXRpb25fbG9jLCBjb2xvcj0icHJlY2lwaXRhdGlvbiIpKSArIHRoZW1lX2J3KCkgKyB4bGFiKCJNb250aCBvZiB5ZWFyIikgKyB5bGFiKCJTdGFuZGFyZGl6ZWQgbWVhbiBtb250aHkgdmFsdWUiKQoKcmVxdWlyZShncmlkRXh0cmEpCmdyaWQuYXJyYW5nZSh0cGxvdCwgdHBwbG90LCBucm93PTEsIG5jb2w9MikKCgpgYGAKCgpXb3csIHJlYWxseSBoYXJkIHRvIHNlZSBtdWNoIGNvbnNpc3RlbmN5LiAKCgo=